Pimcommon

ldapsearchdialog.cpp
1/*
2 * This file is part of libkldap.
3 *
4 * SPDX-FileCopyrightText: 2002 Klarälvdalens Datakonsult AB
5 * SPDX-FileContributor: Steffen Hansen <hansen@kde.org>
6 *
7 * SPDX-License-Identifier: LGPL-2.0-or-later
8 */
9
10#include "ldapsearchdialog.h"
11
12#include <KLDAPCore/LdapClient>
13
14#include <KLDAPCore/LdapClientSearchConfig>
15#include <KLDAPCore/LdapSearchClientReadConfigServerJob>
16#include <KLineEditEventHandler>
17#include <Libkdepim/ProgressIndicatorLabel>
18
19#include <QApplication>
20#include <QCheckBox>
21#include <QClipboard>
22#include <QCloseEvent>
23#include <QFrame>
24#include <QGridLayout>
25#include <QGroupBox>
26#include <QHeaderView>
27#include <QLabel>
28#include <QMenu>
29#include <QPair>
30#include <QPointer>
31#include <QPushButton>
32#include <QSortFilterProxyModel>
33#include <QTableView>
34#include <QVBoxLayout>
35
36#include <KCMultiDialog>
37#include <KConfig>
38#include <KConfigGroup>
39#include <KMessageBox>
40#include <KPluginMetaData>
41#include <QComboBox>
42#include <QDialogButtonBox>
43#include <QLineEdit>
44#include <kldapcore/ldapobject.h>
45#include <kldapcore/ldapserver.h>
46
47#include <KConfigGroup>
48#include <KGuiItem>
49#include <KLocalizedString>
50#include <QDialogButtonBox>
51#include <QLocale>
52
53using namespace PimCommon;
54static QString asUtf8(const QByteArray &val)
55{
56 if (val.isEmpty()) {
57 return {};
58 }
59
60 const char *data = val.data();
61
62 // QString::fromUtf8() bug workaround
63 if (data[val.size() - 1] == '\0') {
64 return QString::fromUtf8(data, val.size() - 1);
65 } else {
66 return QString::fromUtf8(data, val.size());
67 }
68}
69
70static QString join(const KLDAPCore::LdapAttrValue &lst, const QString &sep)
71{
72 QString res;
73 bool already = false;
74 KLDAPCore::LdapAttrValue::ConstIterator end(lst.constEnd());
75 for (KLDAPCore::LdapAttrValue::ConstIterator it = lst.constBegin(); it != end; ++it) {
76 if (already) {
77 res += sep;
78 }
79
80 already = true;
81 res += asUtf8(*it);
82 }
83
84 return res;
85}
86
87static QMap<QString, QString> &adrbookattr2ldap()
88{
89 static QMap<QString, QString> keys;
90
91 if (keys.isEmpty()) {
92 keys[i18nc("@item LDAP search key", "Title")] = QStringLiteral("title");
93 keys[i18n("Full Name")] = QStringLiteral("cn");
94 keys[i18nc("@item LDAP search key", "Email")] = QStringLiteral("mail");
95 keys[i18n("Home Number")] = QStringLiteral("homePhone");
96 keys[i18n("Work Number")] = QStringLiteral("telephoneNumber");
97 keys[i18n("Mobile Number")] = QStringLiteral("mobile");
98 keys[i18n("Fax Number")] = QStringLiteral("facsimileTelephoneNumber");
99 keys[i18n("Pager")] = QStringLiteral("pager");
100 keys[i18n("Street")] = QStringLiteral("street");
101 keys[i18nc("@item LDAP search key", "State")] = QStringLiteral("st");
102 keys[i18n("Country")] = QStringLiteral("co");
103 keys[i18n("City")] = QStringLiteral("l"); // krazy:exclude=doublequote_chars
104 keys[i18n("Organization")] = QStringLiteral("o"); // krazy:exclude=doublequote_chars
105 keys[i18n("Company")] = QStringLiteral("Company");
106 keys[i18n("Department")] = QStringLiteral("department");
107 keys[i18n("Zip Code")] = QStringLiteral("postalCode");
108 keys[i18n("Postal Address")] = QStringLiteral("postalAddress");
109 keys[i18n("Description")] = QStringLiteral("description");
110 keys[i18n("User ID")] = QStringLiteral("uid");
111 }
112
113 return keys;
114}
115
116static QString makeFilter(const QString &query, LdapSearchDialog::FilterType attr, bool startsWith)
117{
118 /* The reasoning behind this filter is:
119 * If it's a person, or a distlist, show it, even if it doesn't have an email address.
120 * If it's not a person, or a distlist, only show it if it has an email attribute.
121 * This allows both resource accounts with an email address which are not a person and
122 * person entries without an email address to show up, while still not showing things
123 * like structural entries in the ldap tree. */
124 QString result(QStringLiteral("&(|(objectclass=person)(objectclass=groupofnames)(mail=*))("));
125 if (query.isEmpty()) {
126 // Return a filter that matches everything
127 return result + QStringLiteral("|(cn=*)(sn=*)") + QLatin1Char(')');
128 }
129
130 if (attr == LdapSearchDialog::Name) {
131 result += startsWith ? QStringLiteral("|(cn=%1*)(sn=%2*)") : QStringLiteral("|(cn=*%1*)(sn=*%2*)");
132 result = result.arg(query, query);
133 } else {
134 result += startsWith ? QStringLiteral("%1=%2*") : QStringLiteral("%1=*%2*");
135 if (attr == LdapSearchDialog::Email) {
136 result = result.arg(QStringLiteral("mail"), query);
137 } else if (attr == LdapSearchDialog::HomeNumber) {
138 result = result.arg(QStringLiteral("homePhone"), query);
139 } else if (attr == LdapSearchDialog::WorkNumber) {
140 result = result.arg(QStringLiteral("telephoneNumber"), query);
141 } else {
142 // Error?
143 result.clear();
144 return result;
145 }
146 }
147 result += QLatin1Char(')');
148 return result;
149}
150
151static KContacts::Addressee convertLdapAttributesToAddressee(const KLDAPCore::LdapAttrMap &attrs)
152{
154
155 // name
156 if (!attrs.value(QStringLiteral("cn")).isEmpty()) {
157 addr.setNameFromString(asUtf8(attrs[QStringLiteral("cn")].first()));
158 }
159
160 // email
161 KLDAPCore::LdapAttrValue lst = attrs[QStringLiteral("mail")];
162 KLDAPCore::LdapAttrValue::ConstIterator it = lst.constBegin();
163 bool pref = true;
164 while (it != lst.constEnd()) {
165 KContacts::Email email(asUtf8(*it));
166 email.setPreferred(pref);
167 addr.addEmail(email);
168 pref = false;
169 ++it;
170 }
171
172 if (!attrs.value(QStringLiteral("o")).isEmpty()) {
173 addr.setOrganization(asUtf8(attrs[QStringLiteral("o")].first()));
174 }
175 if (addr.organization().isEmpty() && !attrs.value(QStringLiteral("Company")).isEmpty()) {
176 addr.setOrganization(asUtf8(attrs[QStringLiteral("Company")].first()));
177 }
178
179 // Address
181
182 if (!attrs.value(QStringLiteral("department")).isEmpty()) {
183 addr.setDepartment(asUtf8(attrs[QStringLiteral("department")].first()));
184 }
185
186 if (!workAddr.isEmpty()) {
187 addr.insertAddress(workAddr);
188 }
189
190 // phone
191 if (!attrs.value(QStringLiteral("homePhone")).isEmpty()) {
192 KContacts::PhoneNumber homeNr = asUtf8(attrs[QStringLiteral("homePhone")].first());
194 addr.insertPhoneNumber(homeNr);
195 }
196
197 if (!attrs.value(QStringLiteral("telephoneNumber")).isEmpty()) {
198 KContacts::PhoneNumber workNr = asUtf8(attrs[QStringLiteral("telephoneNumber")].first());
200 addr.insertPhoneNumber(workNr);
201 }
202
203 if (!attrs.value(QStringLiteral("facsimileTelephoneNumber")).isEmpty()) {
204 KContacts::PhoneNumber faxNr = asUtf8(attrs[QStringLiteral("facsimileTelephoneNumber")].first());
206 addr.insertPhoneNumber(faxNr);
207 }
208
209 if (!attrs.value(QStringLiteral("mobile")).isEmpty()) {
210 KContacts::PhoneNumber cellNr = asUtf8(attrs[QStringLiteral("mobile")].first());
212 addr.insertPhoneNumber(cellNr);
213 }
214
215 if (!attrs.value(QStringLiteral("pager")).isEmpty()) {
216 KContacts::PhoneNumber pagerNr = asUtf8(attrs[QStringLiteral("pager")].first());
218 addr.insertPhoneNumber(pagerNr);
219 }
220
221 return addr;
222}
223
224class ContactListModel : public QAbstractTableModel
225{
226public:
227 enum Role { ServerRole = Qt::UserRole + 1 };
228
229 explicit ContactListModel(QObject *parent)
231 {
232 }
233
234 void addContact(const KLDAPCore::LdapAttrMap &contact, const QString &server)
235 {
237 mContactList.append(contact);
238 mServerList.append(server);
240 }
241
242 [[nodiscard]] QPair<KLDAPCore::LdapAttrMap, QString> contact(const QModelIndex &index) const
243 {
244 if (!index.isValid() || index.row() < 0 || index.row() >= mContactList.count()) {
245 return qMakePair(KLDAPCore::LdapAttrMap(), QString());
246 }
247
248 return qMakePair(mContactList.at(index.row()), mServerList.at(index.row()));
249 }
250
251 [[nodiscard]] QString email(const QModelIndex &index) const
252 {
253 if (!index.isValid() || index.row() < 0 || index.row() >= mContactList.count()) {
254 return {};
255 }
256
257 return asUtf8(mContactList.at(index.row()).value(QStringLiteral("mail")).first()).trimmed();
258 }
259
260 [[nodiscard]] QString fullName(const QModelIndex &index) const
261 {
262 if (!index.isValid() || index.row() < 0 || index.row() >= mContactList.count()) {
263 return {};
264 }
265
266 return asUtf8(mContactList.at(index.row()).value(QStringLiteral("cn")).first()).trimmed();
267 }
268
269 void clear()
270 {
272 mContactList.clear();
273 mServerList.clear();
275 }
276
277 [[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override
278 {
279 if (!parent.isValid()) {
280 return mContactList.count();
281 } else {
282 return 0;
283 }
284 }
285
286 [[nodiscard]] int columnCount(const QModelIndex &parent = QModelIndex()) const override
287 {
288 if (!parent.isValid()) {
289 return 18;
290 } else {
291 return 0;
292 }
293 }
294
295 [[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override
296 {
297 if (orientation == Qt::Vertical || role != Qt::DisplayRole || section < 0 || section > 17) {
298 return {};
299 }
300
301 switch (section) {
302 case 0:
303 return i18n("Full Name");
304 case 1:
305 return i18nc("@title:column Column containing email addresses", "Email");
306 case 2:
307 return i18n("Home Number");
308 case 3:
309 return i18n("Work Number");
310 case 4:
311 return i18n("Mobile Number");
312 case 5:
313 return i18n("Fax Number");
314 case 6:
315 return i18n("Company");
316 case 7:
317 return i18n("Organization");
318 case 8:
319 return i18n("Street");
320 case 9:
321 return i18nc("@title:column Column containing the residential state of the address", "State");
322 case 10:
323 return i18n("Country");
324 case 11:
325 return i18n("Zip Code");
326 case 12:
327 return i18n("Postal Address");
328 case 13:
329 return i18n("City");
330 case 14:
331 return i18n("Department");
332 case 15:
333 return i18n("Description");
334 case 16:
335 return i18n("User ID");
336 case 17:
337 return i18nc("@title:column Column containing title of the person", "Title");
338 default:
339 return {};
340 }
341 }
342
343 [[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
344 {
345 if (!index.isValid()) {
346 return {};
347 }
348
349 if (index.row() < 0 || index.row() >= mContactList.count() || index.column() < 0 || index.column() > 17) {
350 return {};
351 }
352
353 if (role == ServerRole) {
354 return mServerList.at(index.row());
355 }
356
357 if ((role != Qt::DisplayRole) && (role != Qt::ToolTipRole)) {
358 return {};
359 }
360
361 const KLDAPCore::LdapAttrMap map = mContactList.at(index.row());
362
363 switch (index.column()) {
364 case 0:
365 return join(map.value(QStringLiteral("cn")), QStringLiteral(", "));
366 case 1:
367 return join(map.value(QStringLiteral("mail")), QStringLiteral(", "));
368 case 2:
369 return join(map.value(QStringLiteral("homePhone")), QStringLiteral(", "));
370 case 3:
371 return join(map.value(QStringLiteral("telephoneNumber")), QStringLiteral(", "));
372 case 4:
373 return join(map.value(QStringLiteral("mobile")), QStringLiteral(", "));
374 case 5:
375 return join(map.value(QStringLiteral("facsimileTelephoneNumber")), QStringLiteral(", "));
376 case 6:
377 return join(map.value(QStringLiteral("Company")), QStringLiteral(", "));
378 case 7:
379 return join(map.value(QStringLiteral("o")), QStringLiteral(", "));
380 case 8:
381 return join(map.value(QStringLiteral("street")), QStringLiteral(", "));
382 case 9:
383 return join(map.value(QStringLiteral("st")), QStringLiteral(", "));
384 case 10:
385 return join(map.value(QStringLiteral("co")), QStringLiteral(", "));
386 case 11:
387 return join(map.value(QStringLiteral("postalCode")), QStringLiteral(", "));
388 case 12:
389 return join(map.value(QStringLiteral("postalAddress")), QStringLiteral(", "));
390 case 13:
391 return join(map.value(QStringLiteral("l")), QStringLiteral(", "));
392 case 14:
393 return join(map.value(QStringLiteral("department")), QStringLiteral(", "));
394 case 15:
395 return join(map.value(QStringLiteral("description")), QStringLiteral(", "));
396 case 16:
397 return join(map.value(QStringLiteral("uid")), QStringLiteral(", "));
398 case 17:
399 return join(map.value(QStringLiteral("title")), QStringLiteral(", "));
400 default:
401 return {};
402 }
403 }
404
405private:
407 QStringList mServerList;
408};
409
410class Q_DECL_HIDDEN LdapSearchDialog::LdapSearchDialogPrivate
411{
412public:
413 LdapSearchDialogPrivate(LdapSearchDialog *qq)
414 : q(qq)
415 {
416 }
417
419 {
421
422 const QModelIndexList selected = mResultView->selectionModel()->selectedRows();
423 const int numberOfSelectedElement(selected.count());
424 contacts.reserve(numberOfSelectedElement);
425 for (int i = 0; i < numberOfSelectedElement; ++i) {
426 contacts.append(mModel->contact(sortproxy->mapToSource(selected.at(i))));
427 }
428
429 return contacts;
430 }
431
432 void saveSettings();
433 void restoreSettings();
434 void cancelQuery();
435
436 void slotAddResult(const KLDAPCore::LdapClient &, const KLDAPCore::LdapObject &);
437 void slotSetScope(bool);
438 void slotStartSearch();
439 void slotStopSearch();
440 void slotSearchDone();
441 void slotError(const QString &);
442 void slotSelectAll();
443 void slotUnselectAll();
444 void slotSelectionChanged();
445
446 LdapSearchDialog *const q;
447 KGuiItem startSearchGuiItem;
448 KGuiItem stopSearchGuiItem;
449 int mNumHosts = 0;
450 QList<KLDAPCore::LdapClient *> mLdapClientList;
451 bool mIsConfigured = false;
452 KContacts::Addressee::List mSelectedContacts;
453
454 QComboBox *mFilterCombo = nullptr;
455 QComboBox *mSearchType = nullptr;
456 QLineEdit *mSearchEdit = nullptr;
457
458 QCheckBox *mRecursiveCheckbox = nullptr;
459 QTableView *mResultView = nullptr;
460 QPushButton *mSearchButton = nullptr;
461 ContactListModel *mModel = nullptr;
462 KPIM::ProgressIndicatorLabel *progressIndication = nullptr;
463 QSortFilterProxyModel *sortproxy = nullptr;
464 QLineEdit *searchLine = nullptr;
465 QPushButton *user1Button = nullptr;
466};
467
469 : QDialog(parent)
470 , d(new LdapSearchDialogPrivate(this))
471{
472 setWindowTitle(i18nc("@title:window", "Import Contacts from LDAP"));
473 auto mainLayout = new QVBoxLayout(this);
474
475 auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel, this);
476 d->user1Button = new QPushButton;
477 buttonBox->addButton(d->user1Button, QDialogButtonBox::ActionRole);
478
479 auto user2Button = new QPushButton;
480 buttonBox->addButton(user2Button, QDialogButtonBox::ActionRole);
481
482 connect(d->user1Button, &QPushButton::clicked, this, &LdapSearchDialog::slotUser1);
483 connect(user2Button, &QPushButton::clicked, this, &LdapSearchDialog::slotUser2);
484 connect(buttonBox, &QDialogButtonBox::rejected, this, &LdapSearchDialog::slotCancelClicked);
485 d->user1Button->setDefault(true);
486 setModal(false);
488 auto page = new QFrame(this);
489 mainLayout->addWidget(page);
490 mainLayout->addWidget(buttonBox);
491
492 auto topLayout = new QVBoxLayout(page);
493 topLayout->setContentsMargins({});
494
495 auto groupBox = new QGroupBox(i18n("Search for Addresses in Directory"), page);
496 auto boxLayout = new QGridLayout();
497 groupBox->setLayout(boxLayout);
498 boxLayout->setColumnStretch(1, 1);
499
500 auto label = new QLabel(i18nc("@label:textbox", "Search for:"), groupBox);
501 boxLayout->addWidget(label, 0, 0);
502
503 d->mSearchEdit = new QLineEdit(groupBox);
504 d->mSearchEdit->setClearButtonEnabled(true);
505 boxLayout->addWidget(d->mSearchEdit, 0, 1);
506 label->setBuddy(d->mSearchEdit);
507
508 label = new QLabel(i18nc("In LDAP attribute", "in"), groupBox);
509 boxLayout->addWidget(label, 0, 2);
510
511 d->mFilterCombo = new QComboBox(groupBox);
512 d->mFilterCombo->addItem(i18nc("@item:inlistbox Name of the contact", "Name"), QVariant::fromValue(Name));
513 d->mFilterCombo->addItem(i18nc("@item:inlistbox email address of the contact", "Email"), QVariant::fromValue(Email));
514 d->mFilterCombo->addItem(i18nc("@item:inlistbox", "Home Number"), QVariant::fromValue(HomeNumber));
515 d->mFilterCombo->addItem(i18nc("@item:inlistbox", "Work Number"), QVariant::fromValue(WorkNumber));
516 boxLayout->addWidget(d->mFilterCombo, 0, 3);
517 d->startSearchGuiItem = KGuiItem(i18nc("@action:button Start searching", "&Search"), QStringLiteral("edit-find"));
518 d->stopSearchGuiItem = KStandardGuiItem::stop();
519
520 QSize buttonSize;
521 d->mSearchButton = new QPushButton(groupBox);
522 KGuiItem::assign(d->mSearchButton, d->startSearchGuiItem);
523
524 buttonSize = d->mSearchButton->sizeHint();
525 if (buttonSize.width() < d->mSearchButton->sizeHint().width()) {
526 buttonSize = d->mSearchButton->sizeHint();
527 }
528 d->mSearchButton->setFixedWidth(buttonSize.width());
529
530 d->mSearchButton->setDefault(true);
531 boxLayout->addWidget(d->mSearchButton, 0, 4);
532
533 d->mRecursiveCheckbox = new QCheckBox(i18n("Recursive search"), groupBox);
534 d->mRecursiveCheckbox->setChecked(true);
535 boxLayout->addWidget(d->mRecursiveCheckbox, 1, 0, 1, 5);
536
537 d->mSearchType = new QComboBox(groupBox);
538 d->mSearchType->addItem(i18n("Contains"));
539 d->mSearchType->addItem(i18n("Starts With"));
540 boxLayout->addWidget(d->mSearchType, 1, 3, 1, 2);
541
542 topLayout->addWidget(groupBox);
543
544 auto quickSearchLineLayout = new QHBoxLayout;
545 quickSearchLineLayout->addStretch();
546 d->searchLine = new QLineEdit;
548 d->searchLine->setClearButtonEnabled(true);
549 d->searchLine->setPlaceholderText(i18n("Search in result"));
550 quickSearchLineLayout->addWidget(d->searchLine);
551 topLayout->addLayout(quickSearchLineLayout);
552
553 d->mResultView = new QTableView(page);
554 d->mResultView->setSelectionMode(QTableView::MultiSelection);
555 d->mResultView->setSelectionBehavior(QTableView::SelectRows);
556 d->mModel = new ContactListModel(d->mResultView);
557
558 d->sortproxy = new QSortFilterProxyModel(this);
559 d->sortproxy->setFilterKeyColumn(-1); // Search in all column
560 d->sortproxy->setSourceModel(d->mModel);
561 d->sortproxy->setFilterCaseSensitivity(Qt::CaseInsensitive);
563
564 d->mResultView->setModel(d->sortproxy);
565 d->mResultView->verticalHeader()->hide();
566 d->mResultView->setSortingEnabled(true);
567 d->mResultView->horizontalHeader()->setSortIndicatorShown(true);
568 connect(d->mResultView, &QTableView::clicked, this, [this]() {
569 d->slotSelectionChanged();
570 });
571 topLayout->addWidget(d->mResultView);
572
573 d->mResultView->setContextMenuPolicy(Qt::CustomContextMenu);
574 connect(d->mResultView, &QTableView::customContextMenuRequested, this, &LdapSearchDialog::slotCustomContextMenuRequested);
575
576 auto buttonLayout = new QHBoxLayout;
577 buttonLayout->setContentsMargins({});
578 topLayout->addLayout(buttonLayout);
579
580 d->progressIndication = new KPIM::ProgressIndicatorLabel(i18n("Searching..."));
581 buttonLayout->addWidget(d->progressIndication);
582
583 auto buttons = new QDialogButtonBox(page);
584 QPushButton *button = buttons->addButton(i18nc("@action:button", "Select All"), QDialogButtonBox::ActionRole);
585 connect(button, &QPushButton::clicked, this, [this]() {
586 d->slotSelectAll();
587 });
588 button = buttons->addButton(i18nc("@action:button", "Unselect All"), QDialogButtonBox::ActionRole);
589 connect(button, &QPushButton::clicked, this, [this]() {
590 d->slotUnselectAll();
591 });
592
593 buttonLayout->addWidget(buttons);
594
595 d->user1Button->setText(i18n("Add Selected"));
596 user2Button->setText(i18n("Configure LDAP Servers..."));
597
598 connect(d->mRecursiveCheckbox, &QCheckBox::toggled, this, [this](bool state) {
599 d->slotSetScope(state);
600 });
601 connect(d->mSearchButton, SIGNAL(clicked()), this, SLOT(slotStartSearch()));
602
603 setTabOrder(d->mSearchEdit, d->mFilterCombo);
604 setTabOrder(d->mFilterCombo, d->mSearchButton);
605 d->mSearchEdit->setFocus();
606
607 d->slotSelectionChanged();
608 d->restoreSettings();
609}
610
612{
613 d->saveSettings();
614}
615
617{
618 d->mSearchEdit->setText(text);
619}
620
622{
623 return d->mSelectedContacts;
624}
625
626void LdapSearchDialog::slotCustomContextMenuRequested(const QPoint &pos)
627{
628 const QModelIndex index = d->mResultView->indexAt(pos);
629 if (index.isValid()) {
630 QMenu menu(this);
631 QAction *act = menu.addAction(i18n("Copy"));
632 if (menu.exec(QCursor::pos()) == act) {
635 }
636 }
637}
638
639void LdapSearchDialog::LdapSearchDialogPrivate::slotSelectionChanged()
640{
641 user1Button->setEnabled(mResultView->selectionModel()->hasSelection());
642}
643
644void LdapSearchDialog::LdapSearchDialogPrivate::restoreSettings()
645{
646 // Create one KLDAP::LdapClient per selected server and configure it.
647
648 // First clean the list to make sure it is empty at
649 // the beginning of the process
650 qDeleteAll(mLdapClientList);
651 mLdapClientList.clear();
652
653 KConfig *config = KLDAPCore::LdapClientSearchConfig::config();
654
655 KConfigGroup searchGroup(config, QStringLiteral("LDAPSearch"));
656 mSearchType->setCurrentIndex(searchGroup.readEntry("SearchType", 0));
657
658 // then read the config file and register all selected
659 // server in the list
660 KConfigGroup group(config, QStringLiteral("LDAP"));
661 mNumHosts = group.readEntry("NumSelectedHosts", 0);
662 if (!mNumHosts) {
663 mIsConfigured = false;
664 } else {
665 mIsConfigured = true;
666 auto clientSearchConfig = new KLDAPCore::LdapClientSearchConfig;
667 for (int j = 0; j < mNumHosts; ++j) {
668 auto ldapClient = new KLDAPCore::LdapClient(0, q);
669 auto job = new KLDAPCore::LdapSearchClientReadConfigServerJob(q);
670 job->setCurrentIndex(j);
671 job->setActive(true);
672 job->setConfig(group);
673 job->setLdapClient(ldapClient);
674 job->start();
675 QStringList attrs;
676
677 QMap<QString, QString>::ConstIterator end(adrbookattr2ldap().constEnd());
678 for (QMap<QString, QString>::ConstIterator it = adrbookattr2ldap().constBegin(); it != end; ++it) {
679 attrs << *it;
680 }
681
682 ldapClient->setAttributes(attrs);
683
684 // clang-format off
685 q->connect(ldapClient, SIGNAL(result(KLDAPCore::LdapClient,KLDAPCore::LdapObject)), q, SLOT(slotAddResult(KLDAPCore::LdapClient,KLDAPCore::LdapObject)));
686 // clang-format on
687 q->connect(ldapClient, SIGNAL(done()), q, SLOT(slotSearchDone()));
688 q->connect(ldapClient, &KLDAPCore::LdapClient::error, q, [this](const QString &err) {
689 slotError(err);
690 });
691
692 mLdapClientList.append(ldapClient);
693 }
694 delete clientSearchConfig;
695
696 mModel->clear();
697 }
698 KConfigGroup groupHeader(config, QStringLiteral("Headers"));
699 mResultView->horizontalHeader()->restoreState(groupHeader.readEntry("HeaderState", QByteArray()));
700
701 KConfigGroup groupSize(config, QStringLiteral("Size"));
702 const QSize dialogSize = groupSize.readEntry("Size", QSize());
703 if (dialogSize.isValid()) {
704 q->resize(dialogSize);
705 } else {
706 q->resize(QSize(600, 400).expandedTo(q->minimumSizeHint()));
707 }
708}
709
710void LdapSearchDialog::LdapSearchDialogPrivate::saveSettings()
711{
712 KConfig *config = KLDAPCore::LdapClientSearchConfig::config();
713 KConfigGroup group(config, QStringLiteral("LDAPSearch"));
714 group.writeEntry("SearchType", mSearchType->currentIndex());
715
716 KConfigGroup groupHeader(config, QStringLiteral("Headers"));
717 groupHeader.writeEntry("HeaderState", mResultView->horizontalHeader()->saveState());
718 groupHeader.sync();
719
720 KConfigGroup size(config, QStringLiteral("Size"));
721 size.writeEntry("Size", q->size());
722 size.sync();
723
724 group.sync();
725}
726
727void LdapSearchDialog::LdapSearchDialogPrivate::cancelQuery()
728{
729 for (KLDAPCore::LdapClient *client : std::as_const(mLdapClientList)) {
730 client->cancelQuery();
731 }
732}
733
734void LdapSearchDialog::LdapSearchDialogPrivate::slotAddResult(const KLDAPCore::LdapClient &client, const KLDAPCore::LdapObject &obj)
735{
736 mModel->addContact(obj.attributes(), client.server().host());
737}
738
739void LdapSearchDialog::LdapSearchDialogPrivate::slotSetScope(bool rec)
740{
741 for (KLDAPCore::LdapClient *client : std::as_const(mLdapClientList)) {
742 if (rec) {
743 client->setScope(QStringLiteral("sub"));
744 } else {
745 client->setScope(QStringLiteral("one"));
746 }
747 }
748}
749
750void LdapSearchDialog::LdapSearchDialogPrivate::slotStartSearch()
751{
752 cancelQuery();
753
754 if (!mIsConfigured) {
755 KMessageBox::error(q, i18n("You must select an LDAP server before searching."));
756 q->slotUser2();
757 return;
758 }
759
761 KGuiItem::assign(mSearchButton, stopSearchGuiItem);
762 progressIndication->start();
763
764 q->disconnect(mSearchButton, SIGNAL(clicked()), q, SLOT(slotStartSearch()));
765 q->connect(mSearchButton, SIGNAL(clicked()), q, SLOT(slotStopSearch()));
766
767 const bool startsWith = (mSearchType->currentIndex() == 1);
768
769 const QString filter = makeFilter(mSearchEdit->text().trimmed(), mFilterCombo->currentData().value<FilterType>(), startsWith);
770
771 // loop in the list and run the KLDAP::LdapClients
772 mModel->clear();
773 for (KLDAPCore::LdapClient *client : std::as_const(mLdapClientList)) {
774 client->startQuery(filter);
775 }
776
777 saveSettings();
778}
779
780void LdapSearchDialog::LdapSearchDialogPrivate::slotStopSearch()
781{
782 cancelQuery();
783 slotSearchDone();
784}
785
786void LdapSearchDialog::LdapSearchDialogPrivate::slotSearchDone()
787{
788 // If there are no more active clients, we are done.
789 for (KLDAPCore::LdapClient *client : std::as_const(mLdapClientList)) {
790 if (client->isActive()) {
791 return;
792 }
793 }
794
795 q->disconnect(mSearchButton, SIGNAL(clicked()), q, SLOT(slotStopSearch()));
796 q->connect(mSearchButton, SIGNAL(clicked()), q, SLOT(slotStartSearch()));
797
798 KGuiItem::assign(mSearchButton, startSearchGuiItem);
799 progressIndication->stop();
801}
802
803void LdapSearchDialog::LdapSearchDialogPrivate::slotError(const QString &error)
804{
806 KMessageBox::error(q, error);
807}
808
809void LdapSearchDialog::closeEvent(QCloseEvent *e)
810{
811 d->slotStopSearch();
812 e->accept();
813}
814
815void LdapSearchDialog::LdapSearchDialogPrivate::slotUnselectAll()
816{
817 mResultView->clearSelection();
818 slotSelectionChanged();
819}
820
821void LdapSearchDialog::LdapSearchDialogPrivate::slotSelectAll()
822{
823 mResultView->selectAll();
824 slotSelectionChanged();
825}
826
827void LdapSearchDialog::slotUser1()
828{
829 // Import selected items
830
831 d->mSelectedContacts.clear();
832
833 const QList<QPair<KLDAPCore::LdapAttrMap, QString>> &items = d->selectedItems();
834
835 if (!items.isEmpty()) {
837
838 for (int i = 0; i < items.count(); ++i) {
839 KContacts::Addressee contact = convertLdapAttributesToAddressee(items.at(i).first);
840
841 // set a comment where the contact came from
842 contact.setNote(i18nc("arguments are host name, datetime",
843 "Imported from LDAP directory %1 on %2",
844 items.at(i).second,
846
847 d->mSelectedContacts.append(contact);
848 }
849 }
850
851 d->slotStopSearch();
853
854 accept();
855}
856
857void LdapSearchDialog::slotUser2()
858{
859 // Configure LDAP servers
860
861 QPointer<KCMultiDialog> dialog = new KCMultiDialog(this);
862 dialog->setWindowTitle(i18nc("@title:window", "Configure the Address Book LDAP Settings"));
863 dialog->addModule(KPluginMetaData(QStringLiteral("pim6/kcms/kaddressbook/kcm_ldap")));
864
865 if (dialog->exec()) { // krazy:exclude=crashy
866 d->restoreSettings();
867 }
868 delete dialog;
869}
870
871void LdapSearchDialog::slotCancelClicked()
872{
873 d->slotStopSearch();
874 reject();
875}
876
877#include "moc_ldapsearchdialog.cpp"
AddresseeList List
void setNameFromString(const QString &s)
void addEmail(const Email &email)
void setOrganization(const QString &organization)
void insertPhoneNumber(const PhoneNumber &phoneNumber)
QString organization() const
void setDepartment(const QString &department)
void setNote(const QString &note)
void insertAddress(const Address &address)
void setType(Type type)
static void assign(QPushButton *button, const KGuiItem &item)
void startQuery(const QString &filter)
const KLDAPCore::LdapServer server() const
void error(const QString &message)
void setScope(const QString &scope)
bool isActive() const
const LdapAttrMap & attributes() const
QString host() const
A dialog to search contacts in a LDAP directory.
void contactsAdded()
This signal is emitted whenever the user clicked the 'Add Selected' button.
LdapSearchDialog(QWidget *parent=nullptr)
Creates a new ldap search dialog.
KContacts::Addressee::List selectedContacts() const
Returns a list of contacts that have been selected in the LDAP search.
void setSearchText(const QString &text)
Sets the text in the search line edit.
~LdapSearchDialog() override
Destroys the ldap search dialog.
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
char * toString(const EngineQuery &query)
KSERVICE_EXPORT KService::List query(FilterFunc filterFunc)
void catchReturnKey(QObject *lineEdit)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
KGuiItem stop()
KGuiItem close()
const QList< QKeySequence > & end()
folderdialogacltab.h
void clicked(bool checked)
void toggled(bool checked)
void clicked(const QModelIndex &index)
QItemSelectionModel * selectionModel() const const
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const override
void addStretch(int stretch)
char * data()
bool isEmpty() const const
qsizetype size() const const
void setText(const QString &text, Mode mode)
QPoint pos()
QDateTime currentDateTime()
virtual void accept()
virtual void done(int r)
void setModal(bool modal)
virtual void reject()
int result() const const
void accept()
QClipboard * clipboard()
void restoreOverrideCursor()
void setOverrideCursor(const QCursor &cursor)
bool hasSelection() const const
void setContentsMargins(const QMargins &margins)
void textChanged(const QString &text)
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
void clear()
qsizetype count() const const
bool isEmpty() const const
void reserve(qsizetype size)
bool isEmpty() const const
int column() const const
QVariant data(int role) const const
bool isValid() const const
int row() const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QObject * parent() const const
bool isValid() const const
int width() const const
void setFilterFixedString(const QString &pattern)
QString fromUtf8(QByteArrayView str)
bool isEmpty() const const
QString trimmed() const const
CaseInsensitive
CustomContextMenu
WaitCursor
UserRole
Orientation
QFuture< void > filter(QThreadPool *pool, Sequence &sequence, KeepFunctor &&filterFunction)
QFuture< void > map(Iterator begin, Iterator end, MapFunctor &&function)
QVariant fromValue(T &&value)
QString toString() const const
void customContextMenuRequested(const QPoint &pos)
void setEnabled(bool)
void setTabOrder(QWidget *first, QWidget *second)
void setWindowTitle(const QString &)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jun 14 2024 11:48:30 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.