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::FilterType::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::FilterType::Email) {
136 result = result.arg(QStringLiteral("mail"), query);
137 } else if (attr == LdapSearchDialog::FilterType::HomeNumber) {
138 result = result.arg(QStringLiteral("homePhone"), query);
139 } else if (attr == LdapSearchDialog::FilterType::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 {
228 ServerRole = Qt::UserRole + 1
229 };
230
231 explicit ContactListModel(QObject *parent)
233 {
234 }
235
236 void addContact(const KLDAPCore::LdapAttrMap &contact, const QString &server)
237 {
239 mContactList.append(contact);
240 mServerList.append(server);
242 }
243
244 [[nodiscard]] QPair<KLDAPCore::LdapAttrMap, QString> contact(const QModelIndex &index) const
245 {
246 if (!index.isValid() || index.row() < 0 || index.row() >= mContactList.count()) {
247 return qMakePair(KLDAPCore::LdapAttrMap(), QString());
248 }
249
250 return qMakePair(mContactList.at(index.row()), mServerList.at(index.row()));
251 }
252
253 [[nodiscard]] QString email(const QModelIndex &index) const
254 {
255 if (!index.isValid() || index.row() < 0 || index.row() >= mContactList.count()) {
256 return {};
257 }
258
259 return asUtf8(mContactList.at(index.row()).value(QStringLiteral("mail")).first()).trimmed();
260 }
261
262 [[nodiscard]] QString fullName(const QModelIndex &index) const
263 {
264 if (!index.isValid() || index.row() < 0 || index.row() >= mContactList.count()) {
265 return {};
266 }
267
268 return asUtf8(mContactList.at(index.row()).value(QStringLiteral("cn")).first()).trimmed();
269 }
270
271 void clear()
272 {
274 mContactList.clear();
275 mServerList.clear();
277 }
278
279 [[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override
280 {
281 if (!parent.isValid()) {
282 return mContactList.count();
283 } else {
284 return 0;
285 }
286 }
287
288 [[nodiscard]] int columnCount(const QModelIndex &parent = QModelIndex()) const override
289 {
290 if (!parent.isValid()) {
291 return 18;
292 } else {
293 return 0;
294 }
295 }
296
297 [[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override
298 {
299 if (orientation == Qt::Vertical || role != Qt::DisplayRole || section < 0 || section > 17) {
300 return {};
301 }
302
303 switch (section) {
304 case 0:
305 return i18n("Full Name");
306 case 1:
307 return i18nc("@title:column Column containing email addresses", "Email");
308 case 2:
309 return i18n("Home Number");
310 case 3:
311 return i18n("Work Number");
312 case 4:
313 return i18n("Mobile Number");
314 case 5:
315 return i18n("Fax Number");
316 case 6:
317 return i18n("Company");
318 case 7:
319 return i18n("Organization");
320 case 8:
321 return i18n("Street");
322 case 9:
323 return i18nc("@title:column Column containing the residential state of the address", "State");
324 case 10:
325 return i18n("Country");
326 case 11:
327 return i18n("Zip Code");
328 case 12:
329 return i18n("Postal Address");
330 case 13:
331 return i18n("City");
332 case 14:
333 return i18n("Department");
334 case 15:
335 return i18n("Description");
336 case 16:
337 return i18n("User ID");
338 case 17:
339 return i18nc("@title:column Column containing title of the person", "Title");
340 default:
341 return {};
342 }
343 }
344
345 [[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
346 {
347 if (!index.isValid()) {
348 return {};
349 }
350
351 if (index.row() < 0 || index.row() >= mContactList.count() || index.column() < 0 || index.column() > 17) {
352 return {};
353 }
354
355 if (role == ServerRole) {
356 return mServerList.at(index.row());
357 }
358
359 if ((role != Qt::DisplayRole) && (role != Qt::ToolTipRole)) {
360 return {};
361 }
362
363 const KLDAPCore::LdapAttrMap map = mContactList.at(index.row());
364
365 switch (index.column()) {
366 case 0:
367 return join(map.value(QStringLiteral("cn")), QStringLiteral(", "));
368 case 1:
369 return join(map.value(QStringLiteral("mail")), QStringLiteral(", "));
370 case 2:
371 return join(map.value(QStringLiteral("homePhone")), QStringLiteral(", "));
372 case 3:
373 return join(map.value(QStringLiteral("telephoneNumber")), QStringLiteral(", "));
374 case 4:
375 return join(map.value(QStringLiteral("mobile")), QStringLiteral(", "));
376 case 5:
377 return join(map.value(QStringLiteral("facsimileTelephoneNumber")), QStringLiteral(", "));
378 case 6:
379 return join(map.value(QStringLiteral("Company")), QStringLiteral(", "));
380 case 7:
381 return join(map.value(QStringLiteral("o")), QStringLiteral(", "));
382 case 8:
383 return join(map.value(QStringLiteral("street")), QStringLiteral(", "));
384 case 9:
385 return join(map.value(QStringLiteral("st")), QStringLiteral(", "));
386 case 10:
387 return join(map.value(QStringLiteral("co")), QStringLiteral(", "));
388 case 11:
389 return join(map.value(QStringLiteral("postalCode")), QStringLiteral(", "));
390 case 12:
391 return join(map.value(QStringLiteral("postalAddress")), QStringLiteral(", "));
392 case 13:
393 return join(map.value(QStringLiteral("l")), QStringLiteral(", "));
394 case 14:
395 return join(map.value(QStringLiteral("department")), QStringLiteral(", "));
396 case 15:
397 return join(map.value(QStringLiteral("description")), QStringLiteral(", "));
398 case 16:
399 return join(map.value(QStringLiteral("uid")), QStringLiteral(", "));
400 case 17:
401 return join(map.value(QStringLiteral("title")), QStringLiteral(", "));
402 default:
403 return {};
404 }
405 }
406
407private:
409 QStringList mServerList;
410};
411
412class Q_DECL_HIDDEN LdapSearchDialog::LdapSearchDialogPrivate
413{
414public:
415 LdapSearchDialogPrivate(LdapSearchDialog *qq)
416 : q(qq)
417 {
418 }
419
421 {
423
424 const QModelIndexList selected = mResultView->selectionModel()->selectedRows();
425 const int numberOfSelectedElement(selected.count());
426 contacts.reserve(numberOfSelectedElement);
427 for (int i = 0; i < numberOfSelectedElement; ++i) {
428 contacts.append(mModel->contact(sortproxy->mapToSource(selected.at(i))));
429 }
430
431 return contacts;
432 }
433
434 void saveSettings();
435 void restoreSettings();
436 void cancelQuery();
437
438 void slotAddResult(const KLDAPCore::LdapClient &, const KLDAPCore::LdapObject &);
439 void slotSetScope(bool);
440 void slotStartSearch();
441 void slotStopSearch();
442 void slotSearchDone();
443 void slotError(const QString &);
444 void slotSelectAll();
445 void slotUnselectAll();
446 void slotSelectionChanged();
447
448 LdapSearchDialog *const q;
449 KGuiItem startSearchGuiItem;
450 KGuiItem stopSearchGuiItem;
451 int mNumHosts = 0;
452 QList<KLDAPCore::LdapClient *> mLdapClientList;
453 bool mIsConfigured = false;
454 KContacts::Addressee::List mSelectedContacts;
455
456 QComboBox *mFilterCombo = nullptr;
457 QComboBox *mSearchType = nullptr;
458 QLineEdit *mSearchEdit = nullptr;
459
460 QCheckBox *mRecursiveCheckbox = nullptr;
461 QTableView *mResultView = nullptr;
462 QPushButton *mSearchButton = nullptr;
463 ContactListModel *mModel = nullptr;
464 KPIM::ProgressIndicatorLabel *progressIndication = nullptr;
465 QSortFilterProxyModel *sortproxy = nullptr;
466 QLineEdit *searchLine = nullptr;
467 QPushButton *user1Button = nullptr;
468};
469
471 : QDialog(parent)
472 , d(new LdapSearchDialogPrivate(this))
473{
474 setWindowTitle(i18nc("@title:window", "Import Contacts from LDAP"));
475 auto mainLayout = new QVBoxLayout(this);
476
477 auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel, this);
478 d->user1Button = new QPushButton;
479 buttonBox->addButton(d->user1Button, QDialogButtonBox::ActionRole);
480
481 auto user2Button = new QPushButton;
482 buttonBox->addButton(user2Button, QDialogButtonBox::ActionRole);
483
484 connect(d->user1Button, &QPushButton::clicked, this, &LdapSearchDialog::slotUser1);
485 connect(user2Button, &QPushButton::clicked, this, &LdapSearchDialog::slotUser2);
486 connect(buttonBox, &QDialogButtonBox::rejected, this, &LdapSearchDialog::slotCancelClicked);
487 d->user1Button->setDefault(true);
488 setModal(false);
490 auto page = new QFrame(this);
491 mainLayout->addWidget(page);
492 mainLayout->addWidget(buttonBox);
493
494 auto topLayout = new QVBoxLayout(page);
495 topLayout->setContentsMargins({});
496
497 auto groupBox = new QGroupBox(i18n("Search for Addresses in Directory"), page);
498 auto boxLayout = new QGridLayout();
499 groupBox->setLayout(boxLayout);
500 boxLayout->setColumnStretch(1, 1);
501
502 auto label = new QLabel(i18nc("@label:textbox", "Search for:"), groupBox);
503 boxLayout->addWidget(label, 0, 0);
504
505 d->mSearchEdit = new QLineEdit(groupBox);
506 d->mSearchEdit->setClearButtonEnabled(true);
507 boxLayout->addWidget(d->mSearchEdit, 0, 1);
508 label->setBuddy(d->mSearchEdit);
509
510 label = new QLabel(i18nc("In LDAP attribute", "in"), groupBox);
511 boxLayout->addWidget(label, 0, 2);
512
513 d->mFilterCombo = new QComboBox(groupBox);
514 d->mFilterCombo->addItem(i18nc("@item:inlistbox Name of the contact", "Name"), QVariant::fromValue(FilterType::Name));
515 d->mFilterCombo->addItem(i18nc("@item:inlistbox email address of the contact", "Email"), QVariant::fromValue(FilterType::Email));
516 d->mFilterCombo->addItem(i18nc("@item:inlistbox", "Home Number"), QVariant::fromValue(FilterType::HomeNumber));
517 d->mFilterCombo->addItem(i18nc("@item:inlistbox", "Work Number"), QVariant::fromValue(FilterType::WorkNumber));
518 boxLayout->addWidget(d->mFilterCombo, 0, 3);
519 d->startSearchGuiItem = KGuiItem(i18nc("@action:button Start searching", "&Search"), QStringLiteral("edit-find"));
520 d->stopSearchGuiItem = KStandardGuiItem::stop();
521
522 QSize buttonSize;
523 d->mSearchButton = new QPushButton(groupBox);
524 KGuiItem::assign(d->mSearchButton, d->startSearchGuiItem);
525
526 buttonSize = d->mSearchButton->sizeHint();
527 if (buttonSize.width() < d->mSearchButton->sizeHint().width()) {
528 buttonSize = d->mSearchButton->sizeHint();
529 }
530 d->mSearchButton->setFixedWidth(buttonSize.width());
531
532 d->mSearchButton->setDefault(true);
533 boxLayout->addWidget(d->mSearchButton, 0, 4);
534
535 d->mRecursiveCheckbox = new QCheckBox(i18nc("@option:check", "Recursive search"), groupBox);
536 d->mRecursiveCheckbox->setChecked(true);
537 boxLayout->addWidget(d->mRecursiveCheckbox, 1, 0, 1, 5);
538
539 d->mSearchType = new QComboBox(groupBox);
540 d->mSearchType->addItem(i18n("Contains"));
541 d->mSearchType->addItem(i18n("Starts With"));
542 boxLayout->addWidget(d->mSearchType, 1, 3, 1, 2);
543
544 topLayout->addWidget(groupBox);
545
546 auto quickSearchLineLayout = new QHBoxLayout;
547 quickSearchLineLayout->addStretch();
548 d->searchLine = new QLineEdit;
550 d->searchLine->setClearButtonEnabled(true);
551 d->searchLine->setPlaceholderText(i18nc("@info:placeholder", "Search in result"));
552 quickSearchLineLayout->addWidget(d->searchLine);
553 topLayout->addLayout(quickSearchLineLayout);
554
555 d->mResultView = new QTableView(page);
556 d->mResultView->setSelectionMode(QTableView::MultiSelection);
557 d->mResultView->setSelectionBehavior(QTableView::SelectRows);
558 d->mModel = new ContactListModel(d->mResultView);
559
560 d->sortproxy = new QSortFilterProxyModel(this);
561 d->sortproxy->setFilterKeyColumn(-1); // Search in all column
562 d->sortproxy->setSourceModel(d->mModel);
563 d->sortproxy->setFilterCaseSensitivity(Qt::CaseInsensitive);
565
566 d->mResultView->setModel(d->sortproxy);
567 d->mResultView->verticalHeader()->hide();
568 d->mResultView->setSortingEnabled(true);
569 d->mResultView->horizontalHeader()->setSortIndicatorShown(true);
570 connect(d->mResultView, &QTableView::clicked, this, [this]() {
571 d->slotSelectionChanged();
572 });
573 topLayout->addWidget(d->mResultView);
574
575 d->mResultView->setContextMenuPolicy(Qt::CustomContextMenu);
576 connect(d->mResultView, &QTableView::customContextMenuRequested, this, &LdapSearchDialog::slotCustomContextMenuRequested);
577
578 auto buttonLayout = new QHBoxLayout;
579 buttonLayout->setContentsMargins({});
580 topLayout->addLayout(buttonLayout);
581
582 d->progressIndication = new KPIM::ProgressIndicatorLabel(i18n("Searching..."));
583 buttonLayout->addWidget(d->progressIndication);
584
585 auto buttons = new QDialogButtonBox(page);
586 QPushButton *button = buttons->addButton(i18nc("@action:button", "Select All"), QDialogButtonBox::ActionRole);
587 connect(button, &QPushButton::clicked, this, [this]() {
588 d->slotSelectAll();
589 });
590 button = buttons->addButton(i18nc("@action:button", "Unselect All"), QDialogButtonBox::ActionRole);
591 connect(button, &QPushButton::clicked, this, [this]() {
592 d->slotUnselectAll();
593 });
594
595 buttonLayout->addWidget(buttons);
596
597 d->user1Button->setText(i18n("Add Selected"));
598 user2Button->setText(i18n("Configure LDAP Servers..."));
599
600 connect(d->mRecursiveCheckbox, &QCheckBox::toggled, this, [this](bool state) {
601 d->slotSetScope(state);
602 });
603 connect(d->mSearchButton, SIGNAL(clicked()), this, SLOT(slotStartSearch()));
604
605 setTabOrder(d->mSearchEdit, d->mFilterCombo);
606 setTabOrder(d->mFilterCombo, d->mSearchButton);
607 d->mSearchEdit->setFocus();
608
609 d->slotSelectionChanged();
610 d->restoreSettings();
611}
612
614{
615 d->saveSettings();
616}
617
619{
620 d->mSearchEdit->setText(text);
621}
622
624{
625 return d->mSelectedContacts;
626}
627
628void LdapSearchDialog::slotCustomContextMenuRequested(const QPoint &pos)
629{
630 const QModelIndex index = d->mResultView->indexAt(pos);
631 if (index.isValid()) {
632 QMenu menu(this);
633 QAction *act = menu.addAction(i18n("Copy"));
634 if (menu.exec(QCursor::pos()) == act) {
637 }
638 }
639}
640
641void LdapSearchDialog::LdapSearchDialogPrivate::slotSelectionChanged()
642{
643 user1Button->setEnabled(mResultView->selectionModel()->hasSelection());
644}
645
646void LdapSearchDialog::LdapSearchDialogPrivate::restoreSettings()
647{
648 // Create one KLDAP::LdapClient per selected server and configure it.
649
650 // First clean the list to make sure it is empty at
651 // the beginning of the process
652 qDeleteAll(mLdapClientList);
653 mLdapClientList.clear();
654
655 KConfig *config = KLDAPCore::LdapClientSearchConfig::config();
656
657 KConfigGroup searchGroup(config, QStringLiteral("LDAPSearch"));
658 mSearchType->setCurrentIndex(searchGroup.readEntry("SearchType", 0));
659
660 // then read the config file and register all selected
661 // server in the list
662 KConfigGroup group(config, QStringLiteral("LDAP"));
663 mNumHosts = group.readEntry("NumSelectedHosts", 0);
664 if (!mNumHosts) {
665 mIsConfigured = false;
666 } else {
667 mIsConfigured = true;
668 auto clientSearchConfig = new KLDAPCore::LdapClientSearchConfig;
669 for (int j = 0; j < mNumHosts; ++j) {
670 auto ldapClient = new KLDAPCore::LdapClient(0, q);
671 auto job = new KLDAPCore::LdapSearchClientReadConfigServerJob(q);
672 job->setCurrentIndex(j);
673 job->setActive(true);
674 job->setConfig(group);
675 job->setLdapClient(ldapClient);
676 job->start();
677 QStringList attrs;
678
679 QMap<QString, QString>::ConstIterator end(adrbookattr2ldap().constEnd());
680 for (QMap<QString, QString>::ConstIterator it = adrbookattr2ldap().constBegin(); it != end; ++it) {
681 attrs << *it;
682 }
683
684 ldapClient->setAttributes(attrs);
685
686 // clang-format off
687 q->connect(ldapClient, SIGNAL(result(KLDAPCore::LdapClient,KLDAPCore::LdapObject)), q, SLOT(slotAddResult(KLDAPCore::LdapClient,KLDAPCore::LdapObject)));
688 // clang-format on
689 q->connect(ldapClient, SIGNAL(done()), q, SLOT(slotSearchDone()));
690 q->connect(ldapClient, &KLDAPCore::LdapClient::error, q, [this](const QString &err) {
691 slotError(err);
692 });
693
694 mLdapClientList.append(ldapClient);
695 }
696 delete clientSearchConfig;
697
698 mModel->clear();
699 }
700 KConfigGroup groupHeader(config, QStringLiteral("Headers"));
701 mResultView->horizontalHeader()->restoreState(groupHeader.readEntry("HeaderState", QByteArray()));
702
703 KConfigGroup groupSize(config, QStringLiteral("Size"));
704 const QSize dialogSize = groupSize.readEntry("Size", QSize());
705 if (dialogSize.isValid()) {
706 q->resize(dialogSize);
707 } else {
708 q->resize(QSize(600, 400).expandedTo(q->minimumSizeHint()));
709 }
710}
711
712void LdapSearchDialog::LdapSearchDialogPrivate::saveSettings()
713{
714 KConfig *config = KLDAPCore::LdapClientSearchConfig::config();
715 KConfigGroup group(config, QStringLiteral("LDAPSearch"));
716 group.writeEntry("SearchType", mSearchType->currentIndex());
717
718 KConfigGroup groupHeader(config, QStringLiteral("Headers"));
719 groupHeader.writeEntry("HeaderState", mResultView->horizontalHeader()->saveState());
720 groupHeader.sync();
721
722 KConfigGroup size(config, QStringLiteral("Size"));
723 size.writeEntry("Size", q->size());
724 size.sync();
725
726 group.sync();
727}
728
729void LdapSearchDialog::LdapSearchDialogPrivate::cancelQuery()
730{
731 for (KLDAPCore::LdapClient *client : std::as_const(mLdapClientList)) {
732 client->cancelQuery();
733 }
734}
735
736void LdapSearchDialog::LdapSearchDialogPrivate::slotAddResult(const KLDAPCore::LdapClient &client, const KLDAPCore::LdapObject &obj)
737{
738 mModel->addContact(obj.attributes(), client.server().host());
739}
740
741void LdapSearchDialog::LdapSearchDialogPrivate::slotSetScope(bool rec)
742{
743 for (KLDAPCore::LdapClient *client : std::as_const(mLdapClientList)) {
744 if (rec) {
745 client->setScope(QStringLiteral("sub"));
746 } else {
747 client->setScope(QStringLiteral("one"));
748 }
749 }
750}
751
752void LdapSearchDialog::LdapSearchDialogPrivate::slotStartSearch()
753{
754 cancelQuery();
755
756 if (!mIsConfigured) {
757 KMessageBox::error(q, i18n("You must select an LDAP server before searching."));
758 q->slotUser2();
759 return;
760 }
761
763 KGuiItem::assign(mSearchButton, stopSearchGuiItem);
764 progressIndication->start();
765
766 q->disconnect(mSearchButton, SIGNAL(clicked()), q, SLOT(slotStartSearch()));
767 q->connect(mSearchButton, SIGNAL(clicked()), q, SLOT(slotStopSearch()));
768
769 const bool startsWith = (mSearchType->currentIndex() == 1);
770
771 const QString filter = makeFilter(mSearchEdit->text().trimmed(), mFilterCombo->currentData().value<FilterType>(), startsWith);
772
773 // loop in the list and run the KLDAP::LdapClients
774 mModel->clear();
775 for (KLDAPCore::LdapClient *client : std::as_const(mLdapClientList)) {
776 client->startQuery(filter);
777 }
778
779 saveSettings();
780}
781
782void LdapSearchDialog::LdapSearchDialogPrivate::slotStopSearch()
783{
784 cancelQuery();
785 slotSearchDone();
786}
787
788void LdapSearchDialog::LdapSearchDialogPrivate::slotSearchDone()
789{
790 // If there are no more active clients, we are done.
791 for (KLDAPCore::LdapClient *client : std::as_const(mLdapClientList)) {
792 if (client->isActive()) {
793 return;
794 }
795 }
796
797 q->disconnect(mSearchButton, SIGNAL(clicked()), q, SLOT(slotStopSearch()));
798 q->connect(mSearchButton, SIGNAL(clicked()), q, SLOT(slotStartSearch()));
799
800 KGuiItem::assign(mSearchButton, startSearchGuiItem);
801 progressIndication->stop();
803}
804
805void LdapSearchDialog::LdapSearchDialogPrivate::slotError(const QString &error)
806{
808 KMessageBox::error(q, error);
809}
810
811void LdapSearchDialog::closeEvent(QCloseEvent *e)
812{
813 d->slotStopSearch();
814 e->accept();
815}
816
817void LdapSearchDialog::LdapSearchDialogPrivate::slotUnselectAll()
818{
819 mResultView->clearSelection();
820 slotSelectionChanged();
821}
822
823void LdapSearchDialog::LdapSearchDialogPrivate::slotSelectAll()
824{
825 mResultView->selectAll();
826 slotSelectionChanged();
827}
828
829void LdapSearchDialog::slotUser1()
830{
831 // Import selected items
832
833 d->mSelectedContacts.clear();
834
835 const QList<QPair<KLDAPCore::LdapAttrMap, QString>> &items = d->selectedItems();
836
837 if (!items.isEmpty()) {
839
840 for (int i = 0; i < items.count(); ++i) {
841 KContacts::Addressee contact = convertLdapAttributesToAddressee(items.at(i).first);
842
843 // set a comment where the contact came from
844 contact.setNote(i18nc("arguments are host name, datetime",
845 "Imported from LDAP directory %1 on %2",
846 items.at(i).second,
848
849 d->mSelectedContacts.append(contact);
850 }
851 }
852
853 d->slotStopSearch();
855
856 accept();
857}
858
859void LdapSearchDialog::slotUser2()
860{
861 // Configure LDAP servers
862
863 QPointer<KCMultiDialog> dialog = new KCMultiDialog(this);
864 dialog->setWindowTitle(i18nc("@title:window", "Configure the Address Book LDAP Settings"));
865 dialog->addModule(KPluginMetaData(QStringLiteral("pim6/kcms/kaddressbook/kcm_ldap")));
866
867 if (dialog->exec()) { // krazy:exclude=crashy
868 d->restoreSettings();
869 }
870 delete dialog;
871}
872
873void LdapSearchDialog::slotCancelClicked()
874{
875 d->slotStopSearch();
876 reject();
877}
878
879#include "moc_ldapsearchdialog.cpp"
void setNameFromString(const QString &s)
void addEmail(const Email &email)
void setOrganization(const QString &organization)
void insertPhoneNumber(const PhoneNumber &phoneNumber)
QString organization() const
AddresseeList List
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)
ConstIterator
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 Mon Nov 18 2024 12:14:35 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.