File indexing completed on 2025-01-05 04:58:22
0001 /* 0002 * This file is part of libkldap. 0003 * 0004 * SPDX-FileCopyrightText: 2002 Klarälvdalens Datakonsult AB 0005 * SPDX-FileContributor: Steffen Hansen <hansen@kde.org> 0006 * 0007 * SPDX-License-Identifier: LGPL-2.0-or-later 0008 */ 0009 0010 #include "ldapsearchdialog.h" 0011 0012 #include <KLDAPWidgets/LdapClient> 0013 0014 #include <KLDAPWidgets/LdapClientSearchConfig> 0015 #include <KLDAPWidgets/LdapSearchClientReadConfigServerJob> 0016 #include <KLineEditEventHandler> 0017 #include <Libkdepim/ProgressIndicatorLabel> 0018 0019 #include <QApplication> 0020 #include <QCheckBox> 0021 #include <QClipboard> 0022 #include <QCloseEvent> 0023 #include <QFrame> 0024 #include <QGridLayout> 0025 #include <QGroupBox> 0026 #include <QHeaderView> 0027 #include <QLabel> 0028 #include <QMenu> 0029 #include <QPair> 0030 #include <QPointer> 0031 #include <QPushButton> 0032 #include <QSortFilterProxyModel> 0033 #include <QTableView> 0034 #include <QVBoxLayout> 0035 0036 #include <KCMultiDialog> 0037 #include <KConfig> 0038 #include <KConfigGroup> 0039 #include <KMessageBox> 0040 #include <KPluginMetaData> 0041 #include <QComboBox> 0042 #include <QDialogButtonBox> 0043 #include <QLineEdit> 0044 #include <kldapcore/ldapobject.h> 0045 #include <kldapcore/ldapserver.h> 0046 0047 #include <KConfigGroup> 0048 #include <KGuiItem> 0049 #include <KLocalizedString> 0050 #include <QDialogButtonBox> 0051 #include <QLocale> 0052 0053 using namespace PimCommon; 0054 static QString asUtf8(const QByteArray &val) 0055 { 0056 if (val.isEmpty()) { 0057 return {}; 0058 } 0059 0060 const char *data = val.data(); 0061 0062 // QString::fromUtf8() bug workaround 0063 if (data[val.size() - 1] == '\0') { 0064 return QString::fromUtf8(data, val.size() - 1); 0065 } else { 0066 return QString::fromUtf8(data, val.size()); 0067 } 0068 } 0069 0070 static QString join(const KLDAPCore::LdapAttrValue &lst, const QString &sep) 0071 { 0072 QString res; 0073 bool already = false; 0074 KLDAPCore::LdapAttrValue::ConstIterator end(lst.constEnd()); 0075 for (KLDAPCore::LdapAttrValue::ConstIterator it = lst.constBegin(); it != end; ++it) { 0076 if (already) { 0077 res += sep; 0078 } 0079 0080 already = true; 0081 res += asUtf8(*it); 0082 } 0083 0084 return res; 0085 } 0086 0087 static QMap<QString, QString> &adrbookattr2ldap() 0088 { 0089 static QMap<QString, QString> keys; 0090 0091 if (keys.isEmpty()) { 0092 keys[i18nc("@item LDAP search key", "Title")] = QStringLiteral("title"); 0093 keys[i18n("Full Name")] = QStringLiteral("cn"); 0094 keys[i18nc("@item LDAP search key", "Email")] = QStringLiteral("mail"); 0095 keys[i18n("Home Number")] = QStringLiteral("homePhone"); 0096 keys[i18n("Work Number")] = QStringLiteral("telephoneNumber"); 0097 keys[i18n("Mobile Number")] = QStringLiteral("mobile"); 0098 keys[i18n("Fax Number")] = QStringLiteral("facsimileTelephoneNumber"); 0099 keys[i18n("Pager")] = QStringLiteral("pager"); 0100 keys[i18n("Street")] = QStringLiteral("street"); 0101 keys[i18nc("@item LDAP search key", "State")] = QStringLiteral("st"); 0102 keys[i18n("Country")] = QStringLiteral("co"); 0103 keys[i18n("City")] = QStringLiteral("l"); // krazy:exclude=doublequote_chars 0104 keys[i18n("Organization")] = QStringLiteral("o"); // krazy:exclude=doublequote_chars 0105 keys[i18n("Company")] = QStringLiteral("Company"); 0106 keys[i18n("Department")] = QStringLiteral("department"); 0107 keys[i18n("Zip Code")] = QStringLiteral("postalCode"); 0108 keys[i18n("Postal Address")] = QStringLiteral("postalAddress"); 0109 keys[i18n("Description")] = QStringLiteral("description"); 0110 keys[i18n("User ID")] = QStringLiteral("uid"); 0111 } 0112 0113 return keys; 0114 } 0115 0116 static QString makeFilter(const QString &query, LdapSearchDialog::FilterType attr, bool startsWith) 0117 { 0118 /* The reasoning behind this filter is: 0119 * If it's a person, or a distlist, show it, even if it doesn't have an email address. 0120 * If it's not a person, or a distlist, only show it if it has an email attribute. 0121 * This allows both resource accounts with an email address which are not a person and 0122 * person entries without an email address to show up, while still not showing things 0123 * like structural entries in the ldap tree. */ 0124 QString result(QStringLiteral("&(|(objectclass=person)(objectclass=groupofnames)(mail=*))(")); 0125 if (query.isEmpty()) { 0126 // Return a filter that matches everything 0127 return result + QStringLiteral("|(cn=*)(sn=*)") + QLatin1Char(')'); 0128 } 0129 0130 if (attr == LdapSearchDialog::Name) { 0131 result += startsWith ? QStringLiteral("|(cn=%1*)(sn=%2*)") : QStringLiteral("|(cn=*%1*)(sn=*%2*)"); 0132 result = result.arg(query, query); 0133 } else { 0134 result += startsWith ? QStringLiteral("%1=%2*") : QStringLiteral("%1=*%2*"); 0135 if (attr == LdapSearchDialog::Email) { 0136 result = result.arg(QStringLiteral("mail"), query); 0137 } else if (attr == LdapSearchDialog::HomeNumber) { 0138 result = result.arg(QStringLiteral("homePhone"), query); 0139 } else if (attr == LdapSearchDialog::WorkNumber) { 0140 result = result.arg(QStringLiteral("telephoneNumber"), query); 0141 } else { 0142 // Error? 0143 result.clear(); 0144 return result; 0145 } 0146 } 0147 result += QLatin1Char(')'); 0148 return result; 0149 } 0150 0151 static KContacts::Addressee convertLdapAttributesToAddressee(const KLDAPCore::LdapAttrMap &attrs) 0152 { 0153 KContacts::Addressee addr; 0154 0155 // name 0156 if (!attrs.value(QStringLiteral("cn")).isEmpty()) { 0157 addr.setNameFromString(asUtf8(attrs[QStringLiteral("cn")].first())); 0158 } 0159 0160 // email 0161 KLDAPCore::LdapAttrValue lst = attrs[QStringLiteral("mail")]; 0162 KLDAPCore::LdapAttrValue::ConstIterator it = lst.constBegin(); 0163 bool pref = true; 0164 while (it != lst.constEnd()) { 0165 KContacts::Email email(asUtf8(*it)); 0166 email.setPreferred(pref); 0167 addr.addEmail(email); 0168 pref = false; 0169 ++it; 0170 } 0171 0172 if (!attrs.value(QStringLiteral("o")).isEmpty()) { 0173 addr.setOrganization(asUtf8(attrs[QStringLiteral("o")].first())); 0174 } 0175 if (addr.organization().isEmpty() && !attrs.value(QStringLiteral("Company")).isEmpty()) { 0176 addr.setOrganization(asUtf8(attrs[QStringLiteral("Company")].first())); 0177 } 0178 0179 // Address 0180 KContacts::Address workAddr(KContacts::Address::Work); 0181 0182 if (!attrs.value(QStringLiteral("department")).isEmpty()) { 0183 addr.setDepartment(asUtf8(attrs[QStringLiteral("department")].first())); 0184 } 0185 0186 if (!workAddr.isEmpty()) { 0187 addr.insertAddress(workAddr); 0188 } 0189 0190 // phone 0191 if (!attrs.value(QStringLiteral("homePhone")).isEmpty()) { 0192 KContacts::PhoneNumber homeNr = asUtf8(attrs[QStringLiteral("homePhone")].first()); 0193 homeNr.setType(KContacts::PhoneNumber::Home); 0194 addr.insertPhoneNumber(homeNr); 0195 } 0196 0197 if (!attrs.value(QStringLiteral("telephoneNumber")).isEmpty()) { 0198 KContacts::PhoneNumber workNr = asUtf8(attrs[QStringLiteral("telephoneNumber")].first()); 0199 workNr.setType(KContacts::PhoneNumber::Work); 0200 addr.insertPhoneNumber(workNr); 0201 } 0202 0203 if (!attrs.value(QStringLiteral("facsimileTelephoneNumber")).isEmpty()) { 0204 KContacts::PhoneNumber faxNr = asUtf8(attrs[QStringLiteral("facsimileTelephoneNumber")].first()); 0205 faxNr.setType(KContacts::PhoneNumber::Fax); 0206 addr.insertPhoneNumber(faxNr); 0207 } 0208 0209 if (!attrs.value(QStringLiteral("mobile")).isEmpty()) { 0210 KContacts::PhoneNumber cellNr = asUtf8(attrs[QStringLiteral("mobile")].first()); 0211 cellNr.setType(KContacts::PhoneNumber::Cell); 0212 addr.insertPhoneNumber(cellNr); 0213 } 0214 0215 if (!attrs.value(QStringLiteral("pager")).isEmpty()) { 0216 KContacts::PhoneNumber pagerNr = asUtf8(attrs[QStringLiteral("pager")].first()); 0217 pagerNr.setType(KContacts::PhoneNumber::Pager); 0218 addr.insertPhoneNumber(pagerNr); 0219 } 0220 0221 return addr; 0222 } 0223 0224 class ContactListModel : public QAbstractTableModel 0225 { 0226 public: 0227 enum Role { ServerRole = Qt::UserRole + 1 }; 0228 0229 explicit ContactListModel(QObject *parent) 0230 : QAbstractTableModel(parent) 0231 { 0232 } 0233 0234 void addContact(const KLDAPCore::LdapAttrMap &contact, const QString &server) 0235 { 0236 beginResetModel(); 0237 mContactList.append(contact); 0238 mServerList.append(server); 0239 endResetModel(); 0240 } 0241 0242 [[nodiscard]] QPair<KLDAPCore::LdapAttrMap, QString> contact(const QModelIndex &index) const 0243 { 0244 if (!index.isValid() || index.row() < 0 || index.row() >= mContactList.count()) { 0245 return qMakePair(KLDAPCore::LdapAttrMap(), QString()); 0246 } 0247 0248 return qMakePair(mContactList.at(index.row()), mServerList.at(index.row())); 0249 } 0250 0251 [[nodiscard]] QString email(const QModelIndex &index) const 0252 { 0253 if (!index.isValid() || index.row() < 0 || index.row() >= mContactList.count()) { 0254 return {}; 0255 } 0256 0257 return asUtf8(mContactList.at(index.row()).value(QStringLiteral("mail")).first()).trimmed(); 0258 } 0259 0260 [[nodiscard]] QString fullName(const QModelIndex &index) const 0261 { 0262 if (!index.isValid() || index.row() < 0 || index.row() >= mContactList.count()) { 0263 return {}; 0264 } 0265 0266 return asUtf8(mContactList.at(index.row()).value(QStringLiteral("cn")).first()).trimmed(); 0267 } 0268 0269 void clear() 0270 { 0271 beginResetModel(); 0272 mContactList.clear(); 0273 mServerList.clear(); 0274 endResetModel(); 0275 } 0276 0277 [[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override 0278 { 0279 if (!parent.isValid()) { 0280 return mContactList.count(); 0281 } else { 0282 return 0; 0283 } 0284 } 0285 0286 [[nodiscard]] int columnCount(const QModelIndex &parent = QModelIndex()) const override 0287 { 0288 if (!parent.isValid()) { 0289 return 18; 0290 } else { 0291 return 0; 0292 } 0293 } 0294 0295 [[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override 0296 { 0297 if (orientation == Qt::Vertical || role != Qt::DisplayRole || section < 0 || section > 17) { 0298 return {}; 0299 } 0300 0301 switch (section) { 0302 case 0: 0303 return i18n("Full Name"); 0304 case 1: 0305 return i18nc("@title:column Column containing email addresses", "Email"); 0306 case 2: 0307 return i18n("Home Number"); 0308 case 3: 0309 return i18n("Work Number"); 0310 case 4: 0311 return i18n("Mobile Number"); 0312 case 5: 0313 return i18n("Fax Number"); 0314 case 6: 0315 return i18n("Company"); 0316 case 7: 0317 return i18n("Organization"); 0318 case 8: 0319 return i18n("Street"); 0320 case 9: 0321 return i18nc("@title:column Column containing the residential state of the address", "State"); 0322 case 10: 0323 return i18n("Country"); 0324 case 11: 0325 return i18n("Zip Code"); 0326 case 12: 0327 return i18n("Postal Address"); 0328 case 13: 0329 return i18n("City"); 0330 case 14: 0331 return i18n("Department"); 0332 case 15: 0333 return i18n("Description"); 0334 case 16: 0335 return i18n("User ID"); 0336 case 17: 0337 return i18nc("@title:column Column containing title of the person", "Title"); 0338 default: 0339 return {}; 0340 } 0341 } 0342 0343 [[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override 0344 { 0345 if (!index.isValid()) { 0346 return {}; 0347 } 0348 0349 if (index.row() < 0 || index.row() >= mContactList.count() || index.column() < 0 || index.column() > 17) { 0350 return {}; 0351 } 0352 0353 if (role == ServerRole) { 0354 return mServerList.at(index.row()); 0355 } 0356 0357 if ((role != Qt::DisplayRole) && (role != Qt::ToolTipRole)) { 0358 return {}; 0359 } 0360 0361 const KLDAPCore::LdapAttrMap map = mContactList.at(index.row()); 0362 0363 switch (index.column()) { 0364 case 0: 0365 return join(map.value(QStringLiteral("cn")), QStringLiteral(", ")); 0366 case 1: 0367 return join(map.value(QStringLiteral("mail")), QStringLiteral(", ")); 0368 case 2: 0369 return join(map.value(QStringLiteral("homePhone")), QStringLiteral(", ")); 0370 case 3: 0371 return join(map.value(QStringLiteral("telephoneNumber")), QStringLiteral(", ")); 0372 case 4: 0373 return join(map.value(QStringLiteral("mobile")), QStringLiteral(", ")); 0374 case 5: 0375 return join(map.value(QStringLiteral("facsimileTelephoneNumber")), QStringLiteral(", ")); 0376 case 6: 0377 return join(map.value(QStringLiteral("Company")), QStringLiteral(", ")); 0378 case 7: 0379 return join(map.value(QStringLiteral("o")), QStringLiteral(", ")); 0380 case 8: 0381 return join(map.value(QStringLiteral("street")), QStringLiteral(", ")); 0382 case 9: 0383 return join(map.value(QStringLiteral("st")), QStringLiteral(", ")); 0384 case 10: 0385 return join(map.value(QStringLiteral("co")), QStringLiteral(", ")); 0386 case 11: 0387 return join(map.value(QStringLiteral("postalCode")), QStringLiteral(", ")); 0388 case 12: 0389 return join(map.value(QStringLiteral("postalAddress")), QStringLiteral(", ")); 0390 case 13: 0391 return join(map.value(QStringLiteral("l")), QStringLiteral(", ")); 0392 case 14: 0393 return join(map.value(QStringLiteral("department")), QStringLiteral(", ")); 0394 case 15: 0395 return join(map.value(QStringLiteral("description")), QStringLiteral(", ")); 0396 case 16: 0397 return join(map.value(QStringLiteral("uid")), QStringLiteral(", ")); 0398 case 17: 0399 return join(map.value(QStringLiteral("title")), QStringLiteral(", ")); 0400 default: 0401 return {}; 0402 } 0403 } 0404 0405 private: 0406 QList<KLDAPCore::LdapAttrMap> mContactList; 0407 QStringList mServerList; 0408 }; 0409 0410 class Q_DECL_HIDDEN LdapSearchDialog::LdapSearchDialogPrivate 0411 { 0412 public: 0413 LdapSearchDialogPrivate(LdapSearchDialog *qq) 0414 : q(qq) 0415 { 0416 } 0417 0418 QList<QPair<KLDAPCore::LdapAttrMap, QString>> selectedItems() 0419 { 0420 QList<QPair<KLDAPCore::LdapAttrMap, QString>> contacts; 0421 0422 const QModelIndexList selected = mResultView->selectionModel()->selectedRows(); 0423 const int numberOfSelectedElement(selected.count()); 0424 contacts.reserve(numberOfSelectedElement); 0425 for (int i = 0; i < numberOfSelectedElement; ++i) { 0426 contacts.append(mModel->contact(sortproxy->mapToSource(selected.at(i)))); 0427 } 0428 0429 return contacts; 0430 } 0431 0432 void saveSettings(); 0433 void restoreSettings(); 0434 void cancelQuery(); 0435 0436 void slotAddResult(const KLDAPWidgets::LdapClient &, const KLDAPCore::LdapObject &); 0437 void slotSetScope(bool); 0438 void slotStartSearch(); 0439 void slotStopSearch(); 0440 void slotSearchDone(); 0441 void slotError(const QString &); 0442 void slotSelectAll(); 0443 void slotUnselectAll(); 0444 void slotSelectionChanged(); 0445 0446 LdapSearchDialog *const q; 0447 KGuiItem startSearchGuiItem; 0448 KGuiItem stopSearchGuiItem; 0449 int mNumHosts = 0; 0450 QList<KLDAPWidgets::LdapClient *> mLdapClientList; 0451 bool mIsConfigured = false; 0452 KContacts::Addressee::List mSelectedContacts; 0453 0454 QComboBox *mFilterCombo = nullptr; 0455 QComboBox *mSearchType = nullptr; 0456 QLineEdit *mSearchEdit = nullptr; 0457 0458 QCheckBox *mRecursiveCheckbox = nullptr; 0459 QTableView *mResultView = nullptr; 0460 QPushButton *mSearchButton = nullptr; 0461 ContactListModel *mModel = nullptr; 0462 KPIM::ProgressIndicatorLabel *progressIndication = nullptr; 0463 QSortFilterProxyModel *sortproxy = nullptr; 0464 QLineEdit *searchLine = nullptr; 0465 QPushButton *user1Button = nullptr; 0466 }; 0467 0468 LdapSearchDialog::LdapSearchDialog(QWidget *parent) 0469 : QDialog(parent) 0470 , d(new LdapSearchDialogPrivate(this)) 0471 { 0472 setWindowTitle(i18nc("@title:window", "Import Contacts from LDAP")); 0473 auto mainLayout = new QVBoxLayout(this); 0474 0475 auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel, this); 0476 d->user1Button = new QPushButton; 0477 buttonBox->addButton(d->user1Button, QDialogButtonBox::ActionRole); 0478 0479 auto user2Button = new QPushButton; 0480 buttonBox->addButton(user2Button, QDialogButtonBox::ActionRole); 0481 0482 connect(d->user1Button, &QPushButton::clicked, this, &LdapSearchDialog::slotUser1); 0483 connect(user2Button, &QPushButton::clicked, this, &LdapSearchDialog::slotUser2); 0484 connect(buttonBox, &QDialogButtonBox::rejected, this, &LdapSearchDialog::slotCancelClicked); 0485 d->user1Button->setDefault(true); 0486 setModal(false); 0487 KGuiItem::assign(buttonBox->button(QDialogButtonBox::Cancel), KStandardGuiItem::close()); 0488 auto page = new QFrame(this); 0489 mainLayout->addWidget(page); 0490 mainLayout->addWidget(buttonBox); 0491 0492 auto topLayout = new QVBoxLayout(page); 0493 topLayout->setContentsMargins({}); 0494 0495 auto groupBox = new QGroupBox(i18n("Search for Addresses in Directory"), page); 0496 auto boxLayout = new QGridLayout(); 0497 groupBox->setLayout(boxLayout); 0498 boxLayout->setColumnStretch(1, 1); 0499 0500 auto label = new QLabel(i18n("Search for:"), groupBox); 0501 boxLayout->addWidget(label, 0, 0); 0502 0503 d->mSearchEdit = new QLineEdit(groupBox); 0504 d->mSearchEdit->setClearButtonEnabled(true); 0505 boxLayout->addWidget(d->mSearchEdit, 0, 1); 0506 label->setBuddy(d->mSearchEdit); 0507 0508 label = new QLabel(i18nc("In LDAP attribute", "in"), groupBox); 0509 boxLayout->addWidget(label, 0, 2); 0510 0511 d->mFilterCombo = new QComboBox(groupBox); 0512 d->mFilterCombo->addItem(i18nc("@item:inlistbox Name of the contact", "Name"), QVariant::fromValue(Name)); 0513 d->mFilterCombo->addItem(i18nc("@item:inlistbox email address of the contact", "Email"), QVariant::fromValue(Email)); 0514 d->mFilterCombo->addItem(i18nc("@item:inlistbox", "Home Number"), QVariant::fromValue(HomeNumber)); 0515 d->mFilterCombo->addItem(i18nc("@item:inlistbox", "Work Number"), QVariant::fromValue(WorkNumber)); 0516 boxLayout->addWidget(d->mFilterCombo, 0, 3); 0517 d->startSearchGuiItem = KGuiItem(i18nc("@action:button Start searching", "&Search"), QStringLiteral("edit-find")); 0518 d->stopSearchGuiItem = KStandardGuiItem::stop(); 0519 0520 QSize buttonSize; 0521 d->mSearchButton = new QPushButton(groupBox); 0522 KGuiItem::assign(d->mSearchButton, d->startSearchGuiItem); 0523 0524 buttonSize = d->mSearchButton->sizeHint(); 0525 if (buttonSize.width() < d->mSearchButton->sizeHint().width()) { 0526 buttonSize = d->mSearchButton->sizeHint(); 0527 } 0528 d->mSearchButton->setFixedWidth(buttonSize.width()); 0529 0530 d->mSearchButton->setDefault(true); 0531 boxLayout->addWidget(d->mSearchButton, 0, 4); 0532 0533 d->mRecursiveCheckbox = new QCheckBox(i18n("Recursive search"), groupBox); 0534 d->mRecursiveCheckbox->setChecked(true); 0535 boxLayout->addWidget(d->mRecursiveCheckbox, 1, 0, 1, 5); 0536 0537 d->mSearchType = new QComboBox(groupBox); 0538 d->mSearchType->addItem(i18n("Contains")); 0539 d->mSearchType->addItem(i18n("Starts With")); 0540 boxLayout->addWidget(d->mSearchType, 1, 3, 1, 2); 0541 0542 topLayout->addWidget(groupBox); 0543 0544 auto quickSearchLineLayout = new QHBoxLayout; 0545 quickSearchLineLayout->addStretch(); 0546 d->searchLine = new QLineEdit; 0547 KLineEditEventHandler::catchReturnKey(d->searchLine); 0548 d->searchLine->setClearButtonEnabled(true); 0549 d->searchLine->setPlaceholderText(i18n("Search in result")); 0550 quickSearchLineLayout->addWidget(d->searchLine); 0551 topLayout->addLayout(quickSearchLineLayout); 0552 0553 d->mResultView = new QTableView(page); 0554 d->mResultView->setSelectionMode(QTableView::MultiSelection); 0555 d->mResultView->setSelectionBehavior(QTableView::SelectRows); 0556 d->mModel = new ContactListModel(d->mResultView); 0557 0558 d->sortproxy = new QSortFilterProxyModel(this); 0559 d->sortproxy->setFilterKeyColumn(-1); // Search in all column 0560 d->sortproxy->setSourceModel(d->mModel); 0561 d->sortproxy->setFilterCaseSensitivity(Qt::CaseInsensitive); 0562 connect(d->searchLine, &QLineEdit::textChanged, d->sortproxy, &QSortFilterProxyModel::setFilterFixedString); 0563 0564 d->mResultView->setModel(d->sortproxy); 0565 d->mResultView->verticalHeader()->hide(); 0566 d->mResultView->setSortingEnabled(true); 0567 d->mResultView->horizontalHeader()->setSortIndicatorShown(true); 0568 connect(d->mResultView, &QTableView::clicked, this, [this]() { 0569 d->slotSelectionChanged(); 0570 }); 0571 topLayout->addWidget(d->mResultView); 0572 0573 d->mResultView->setContextMenuPolicy(Qt::CustomContextMenu); 0574 connect(d->mResultView, &QTableView::customContextMenuRequested, this, &LdapSearchDialog::slotCustomContextMenuRequested); 0575 0576 auto buttonLayout = new QHBoxLayout; 0577 buttonLayout->setContentsMargins({}); 0578 topLayout->addLayout(buttonLayout); 0579 0580 d->progressIndication = new KPIM::ProgressIndicatorLabel(i18n("Searching...")); 0581 buttonLayout->addWidget(d->progressIndication); 0582 0583 auto buttons = new QDialogButtonBox(page); 0584 QPushButton *button = buttons->addButton(i18n("Select All"), QDialogButtonBox::ActionRole); 0585 connect(button, &QPushButton::clicked, this, [this]() { 0586 d->slotSelectAll(); 0587 }); 0588 button = buttons->addButton(i18n("Unselect All"), QDialogButtonBox::ActionRole); 0589 connect(button, &QPushButton::clicked, this, [this]() { 0590 d->slotUnselectAll(); 0591 }); 0592 0593 buttonLayout->addWidget(buttons); 0594 0595 d->user1Button->setText(i18n("Add Selected")); 0596 user2Button->setText(i18n("Configure LDAP Servers...")); 0597 0598 connect(d->mRecursiveCheckbox, &QCheckBox::toggled, this, [this](bool state) { 0599 d->slotSetScope(state); 0600 }); 0601 connect(d->mSearchButton, SIGNAL(clicked()), this, SLOT(slotStartSearch())); 0602 0603 setTabOrder(d->mSearchEdit, d->mFilterCombo); 0604 setTabOrder(d->mFilterCombo, d->mSearchButton); 0605 d->mSearchEdit->setFocus(); 0606 0607 d->slotSelectionChanged(); 0608 d->restoreSettings(); 0609 } 0610 0611 LdapSearchDialog::~LdapSearchDialog() 0612 { 0613 d->saveSettings(); 0614 } 0615 0616 void LdapSearchDialog::setSearchText(const QString &text) 0617 { 0618 d->mSearchEdit->setText(text); 0619 } 0620 0621 KContacts::Addressee::List LdapSearchDialog::selectedContacts() const 0622 { 0623 return d->mSelectedContacts; 0624 } 0625 0626 void LdapSearchDialog::slotCustomContextMenuRequested(const QPoint &pos) 0627 { 0628 const QModelIndex index = d->mResultView->indexAt(pos); 0629 if (index.isValid()) { 0630 QMenu menu(this); 0631 QAction *act = menu.addAction(i18n("Copy")); 0632 if (menu.exec(QCursor::pos()) == act) { 0633 QClipboard *cb = QApplication::clipboard(); 0634 cb->setText(index.data().toString(), QClipboard::Clipboard); 0635 } 0636 } 0637 } 0638 0639 void LdapSearchDialog::LdapSearchDialogPrivate::slotSelectionChanged() 0640 { 0641 user1Button->setEnabled(mResultView->selectionModel()->hasSelection()); 0642 } 0643 0644 void LdapSearchDialog::LdapSearchDialogPrivate::restoreSettings() 0645 { 0646 // Create one KLDAP::LdapClient per selected server and configure it. 0647 0648 // First clean the list to make sure it is empty at 0649 // the beginning of the process 0650 qDeleteAll(mLdapClientList); 0651 mLdapClientList.clear(); 0652 0653 KConfig *config = KLDAPWidgets::LdapClientSearchConfig::config(); 0654 0655 KConfigGroup searchGroup(config, QStringLiteral("LDAPSearch")); 0656 mSearchType->setCurrentIndex(searchGroup.readEntry("SearchType", 0)); 0657 0658 // then read the config file and register all selected 0659 // server in the list 0660 KConfigGroup group(config, QStringLiteral("LDAP")); 0661 mNumHosts = group.readEntry("NumSelectedHosts", 0); 0662 if (!mNumHosts) { 0663 mIsConfigured = false; 0664 } else { 0665 mIsConfigured = true; 0666 auto clientSearchConfig = new KLDAPWidgets::LdapClientSearchConfig; 0667 for (int j = 0; j < mNumHosts; ++j) { 0668 auto ldapClient = new KLDAPWidgets::LdapClient(0, q); 0669 auto job = new KLDAPWidgets::LdapSearchClientReadConfigServerJob(q); 0670 job->setCurrentIndex(j); 0671 job->setActive(true); 0672 job->setConfig(group); 0673 job->setLdapClient(ldapClient); 0674 job->start(); 0675 QStringList attrs; 0676 0677 QMap<QString, QString>::ConstIterator end(adrbookattr2ldap().constEnd()); 0678 for (QMap<QString, QString>::ConstIterator it = adrbookattr2ldap().constBegin(); it != end; ++it) { 0679 attrs << *it; 0680 } 0681 0682 ldapClient->setAttributes(attrs); 0683 0684 // clang-format off 0685 q->connect(ldapClient, SIGNAL(result(KLDAPWidgets::LdapClient,KLDAPCore::LdapObject)), q, SLOT(slotAddResult(KLDAPWidgets::LdapClient,KLDAPCore::LdapObject))); 0686 // clang-format on 0687 q->connect(ldapClient, SIGNAL(done()), q, SLOT(slotSearchDone())); 0688 q->connect(ldapClient, &KLDAPWidgets::LdapClient::error, q, [this](const QString &err) { 0689 slotError(err); 0690 }); 0691 0692 mLdapClientList.append(ldapClient); 0693 } 0694 delete clientSearchConfig; 0695 0696 mModel->clear(); 0697 } 0698 KConfigGroup groupHeader(config, QStringLiteral("Headers")); 0699 mResultView->horizontalHeader()->restoreState(groupHeader.readEntry("HeaderState", QByteArray())); 0700 0701 KConfigGroup groupSize(config, QStringLiteral("Size")); 0702 const QSize dialogSize = groupSize.readEntry("Size", QSize()); 0703 if (dialogSize.isValid()) { 0704 q->resize(dialogSize); 0705 } else { 0706 q->resize(QSize(600, 400).expandedTo(q->minimumSizeHint())); 0707 } 0708 } 0709 0710 void LdapSearchDialog::LdapSearchDialogPrivate::saveSettings() 0711 { 0712 KConfig *config = KLDAPWidgets::LdapClientSearchConfig::config(); 0713 KConfigGroup group(config, QStringLiteral("LDAPSearch")); 0714 group.writeEntry("SearchType", mSearchType->currentIndex()); 0715 0716 KConfigGroup groupHeader(config, QStringLiteral("Headers")); 0717 groupHeader.writeEntry("HeaderState", mResultView->horizontalHeader()->saveState()); 0718 groupHeader.sync(); 0719 0720 KConfigGroup size(config, QStringLiteral("Size")); 0721 size.writeEntry("Size", q->size()); 0722 size.sync(); 0723 0724 group.sync(); 0725 } 0726 0727 void LdapSearchDialog::LdapSearchDialogPrivate::cancelQuery() 0728 { 0729 for (KLDAPWidgets::LdapClient *client : std::as_const(mLdapClientList)) { 0730 client->cancelQuery(); 0731 } 0732 } 0733 0734 void LdapSearchDialog::LdapSearchDialogPrivate::slotAddResult(const KLDAPWidgets::LdapClient &client, const KLDAPCore::LdapObject &obj) 0735 { 0736 mModel->addContact(obj.attributes(), client.server().host()); 0737 } 0738 0739 void LdapSearchDialog::LdapSearchDialogPrivate::slotSetScope(bool rec) 0740 { 0741 for (KLDAPWidgets::LdapClient *client : std::as_const(mLdapClientList)) { 0742 if (rec) { 0743 client->setScope(QStringLiteral("sub")); 0744 } else { 0745 client->setScope(QStringLiteral("one")); 0746 } 0747 } 0748 } 0749 0750 void LdapSearchDialog::LdapSearchDialogPrivate::slotStartSearch() 0751 { 0752 cancelQuery(); 0753 0754 if (!mIsConfigured) { 0755 KMessageBox::error(q, i18n("You must select an LDAP server before searching.")); 0756 q->slotUser2(); 0757 return; 0758 } 0759 0760 #ifndef QT_NO_CURSOR 0761 QApplication::setOverrideCursor(Qt::WaitCursor); 0762 #endif 0763 KGuiItem::assign(mSearchButton, stopSearchGuiItem); 0764 progressIndication->start(); 0765 0766 q->disconnect(mSearchButton, SIGNAL(clicked()), q, SLOT(slotStartSearch())); 0767 q->connect(mSearchButton, SIGNAL(clicked()), q, SLOT(slotStopSearch())); 0768 0769 const bool startsWith = (mSearchType->currentIndex() == 1); 0770 0771 const QString filter = makeFilter(mSearchEdit->text().trimmed(), mFilterCombo->currentData().value<FilterType>(), startsWith); 0772 0773 // loop in the list and run the KLDAP::LdapClients 0774 mModel->clear(); 0775 for (KLDAPWidgets::LdapClient *client : std::as_const(mLdapClientList)) { 0776 client->startQuery(filter); 0777 } 0778 0779 saveSettings(); 0780 } 0781 0782 void LdapSearchDialog::LdapSearchDialogPrivate::slotStopSearch() 0783 { 0784 cancelQuery(); 0785 slotSearchDone(); 0786 } 0787 0788 void LdapSearchDialog::LdapSearchDialogPrivate::slotSearchDone() 0789 { 0790 // If there are no more active clients, we are done. 0791 for (KLDAPWidgets::LdapClient *client : std::as_const(mLdapClientList)) { 0792 if (client->isActive()) { 0793 return; 0794 } 0795 } 0796 0797 q->disconnect(mSearchButton, SIGNAL(clicked()), q, SLOT(slotStopSearch())); 0798 q->connect(mSearchButton, SIGNAL(clicked()), q, SLOT(slotStartSearch())); 0799 0800 KGuiItem::assign(mSearchButton, startSearchGuiItem); 0801 progressIndication->stop(); 0802 #ifndef QT_NO_CURSOR 0803 QApplication::restoreOverrideCursor(); 0804 #endif 0805 } 0806 0807 void LdapSearchDialog::LdapSearchDialogPrivate::slotError(const QString &error) 0808 { 0809 #ifndef QT_NO_CURSOR 0810 QApplication::restoreOverrideCursor(); 0811 #endif 0812 KMessageBox::error(q, error); 0813 } 0814 0815 void LdapSearchDialog::closeEvent(QCloseEvent *e) 0816 { 0817 d->slotStopSearch(); 0818 e->accept(); 0819 } 0820 0821 void LdapSearchDialog::LdapSearchDialogPrivate::slotUnselectAll() 0822 { 0823 mResultView->clearSelection(); 0824 slotSelectionChanged(); 0825 } 0826 0827 void LdapSearchDialog::LdapSearchDialogPrivate::slotSelectAll() 0828 { 0829 mResultView->selectAll(); 0830 slotSelectionChanged(); 0831 } 0832 0833 void LdapSearchDialog::slotUser1() 0834 { 0835 // Import selected items 0836 0837 d->mSelectedContacts.clear(); 0838 0839 const QList<QPair<KLDAPCore::LdapAttrMap, QString>> &items = d->selectedItems(); 0840 0841 if (!items.isEmpty()) { 0842 const QDateTime now = QDateTime::currentDateTime(); 0843 0844 for (int i = 0; i < items.count(); ++i) { 0845 KContacts::Addressee contact = convertLdapAttributesToAddressee(items.at(i).first); 0846 0847 // set a comment where the contact came from 0848 contact.setNote(i18nc("arguments are host name, datetime", 0849 "Imported from LDAP directory %1 on %2", 0850 items.at(i).second, 0851 QLocale().toString(now, QLocale::ShortFormat))); 0852 0853 d->mSelectedContacts.append(contact); 0854 } 0855 } 0856 0857 d->slotStopSearch(); 0858 Q_EMIT contactsAdded(); 0859 0860 accept(); 0861 } 0862 0863 void LdapSearchDialog::slotUser2() 0864 { 0865 // Configure LDAP servers 0866 0867 QPointer<KCMultiDialog> dialog = new KCMultiDialog(this); 0868 dialog->setWindowTitle(i18nc("@title:window", "Configure the Address Book LDAP Settings")); 0869 dialog->addModule(KPluginMetaData(QStringLiteral("pim6/kcms/kaddressbook/kcm_ldap"))); 0870 0871 if (dialog->exec()) { // krazy:exclude=crashy 0872 d->restoreSettings(); 0873 } 0874 delete dialog; 0875 } 0876 0877 void LdapSearchDialog::slotCancelClicked() 0878 { 0879 d->slotStopSearch(); 0880 reject(); 0881 } 0882 0883 #include "moc_ldapsearchdialog.cpp"