diff --git a/korganizer/views/collectionview/controller.cpp b/korganizer/views/collectionview/controller.cpp --- a/korganizer/views/collectionview/controller.cpp +++ b/korganizer/views/collectionview/controller.cpp @@ -150,6 +150,9 @@ { if (role == Qt::DisplayRole) { QString name = mPerson.name; + if (!mPerson.group.isEmpty()) { + name = mPerson.group + QLatin1String(" - ") + name; + } if (!mPerson.ou.isEmpty()) { name += QLatin1String(" (") + mPerson.ou + QLatin1String(")"); } @@ -166,6 +169,9 @@ } if (role == Qt::ToolTipRole) { QString tooltip = i18n("Person: ") + mPerson.name; + if (!mPerson.group.isEmpty()) { + tooltip += QLatin1String("\n") + i18n("Group: ") + mPerson.group; + } if (!mPerson.mail.isEmpty()) { tooltip += QLatin1String("\n") + i18n("Mail: ") + mPerson.mail; } diff --git a/libkdepim/job/person.h b/libkdepim/job/person.h --- a/libkdepim/job/person.h +++ b/libkdepim/job/person.h @@ -35,6 +35,7 @@ QString uid; QString ou; QString mail; + QString group; Akonadi::Collection::Id rootCollection; bool updateDisplayName; diff --git a/libkdepim/job/personsearchjob.h b/libkdepim/job/personsearchjob.h --- a/libkdepim/job/personsearchjob.h +++ b/libkdepim/job/personsearchjob.h @@ -30,6 +30,11 @@ #include #include "person.h" +#include + +class ExpandGroupJob; +typedef QSharedPointer ExpandGroupJobPtr; + class KDEPIM_EXPORT PersonSearchJob : public KJob { Q_OBJECT @@ -55,6 +60,10 @@ void onLDAPSearchDone(); void updatePersonCollection(const Person &person); void modifyResult(KJob *job); + void onGroupFound(QString name, QList members); + +private: + bool isGroup(KLDAP::LdapObject object) const; private: QString mSearchString; @@ -62,6 +71,7 @@ KLDAP::LdapClientSearch mLdapSearch; bool mCollectionSearchDone; bool mLdapSearchDone; + QHash mExpandGroupJobs; }; #endif diff --git a/libkdepim/job/personsearchjob.cpp b/libkdepim/job/personsearchjob.cpp --- a/libkdepim/job/personsearchjob.cpp +++ b/libkdepim/job/personsearchjob.cpp @@ -20,19 +20,101 @@ without including the source code for Qt in the source distribution. */ #include "personsearchjob.h" +#include #include #include #include #include +#include +#include #include #include #include +class ExpandGroupJob : public QObject +{ + Q_OBJECT +public: + ExpandGroupJob(const KLDAP::LdapObject &groupObject, const KLDAP::LdapServer &server, const QStringList& attributes); + virtual ~ExpandGroupJob(); + + typedef QSharedPointer Ptr; + + virtual void start(); + const QString &groupName() const; + +Q_SIGNALS: + void groupMembers(const QString groupName, const QList &objects); + +private Q_SLOTS: + void groupResult(KLDAP::LdapSearch *ldapSearch, KLDAP::LdapObject object); + void onResult(KLDAP::LdapSearch *); + +private: + void searchNext(KLDAP::LdapSearch *ldapSearch); + KLDAP::LdapConnection mConnection; + KLDAP::LdapAttrValue mGroupMembersDN; + QString mGroupName; + QStringList mAttributes; + QList mResults; +}; + +ExpandGroupJob::ExpandGroupJob(const KLDAP::LdapObject &groupObject, const KLDAP::LdapServer &server, const QStringList &attributes) +: QObject() +, mConnection(server) +, mAttributes(attributes) +, mGroupMembersDN(groupObject.values(QLatin1String("uniqueMember"))) +, mGroupName(QString::fromUtf8(groupObject.value(QLatin1String("cn")))) +{ +} + +ExpandGroupJob::~ExpandGroupJob() +{ + mConnection.close(); +} + +void ExpandGroupJob::start() +{ + mConnection.connect(); + + KLDAP::LdapSearch *search(new KLDAP::LdapSearch(mConnection)); + connect(search, SIGNAL(data(KLDAP::LdapSearch*,KLDAP::LdapObject)), SLOT(groupResult(KLDAP::LdapSearch*,KLDAP::LdapObject))); + connect(search, SIGNAL(result(KLDAP::LdapSearch*)), SLOT(onResult(KLDAP::LdapSearch*))); + searchNext(search); +} + +const QString &ExpandGroupJob::groupName() const +{ + return mGroupName; +} + +void ExpandGroupJob::groupResult(KLDAP::LdapSearch *ldapSearch, KLDAP::LdapObject object) +{ + mResults << object; +} + +void ExpandGroupJob::onResult(KLDAP::LdapSearch *ldapSearch) +{ + if (mGroupMembersDN.isEmpty()) { + emit groupMembers(groupName(), mResults); + } else { + searchNext(ldapSearch); + } +} + +void ExpandGroupJob::searchNext(KLDAP::LdapSearch *ldapSearch) +{ + ldapSearch->search(KLDAP::LdapDN(QString::fromLatin1(mGroupMembersDN.takeFirst())), KLDAP::LdapUrl::Base, QString(), mAttributes); +} + PersonSearchJob::PersonSearchJob(const QString& searchString, QObject* parent) : KJob(parent), mSearchString(searchString) { + QStringList attrs = mLdapSearch.attributes(); + attrs.append(QLatin1String("uniqueMember")); + mLdapSearch.setAttributes(attrs); connect(&mLdapSearch, SIGNAL(searchData(const QList &)), SLOT(onLDAPSearchData(const QList &))); @@ -47,6 +129,11 @@ bool PersonSearchJob::kill(KJob::KillVerbosity verbosity) { + Q_FOREACH(ExpandGroupJob::Ptr job, mExpandGroupJobs) { + disconnect(job.data()); + } + mExpandGroupJobs.clear(); + mLdapSearch.cancelSearch(); return KJob::kill(verbosity); } @@ -71,6 +158,12 @@ mCollectionSearchDone = true; } + Q_FOREACH(ExpandGroupJob::Ptr job, mExpandGroupJobs) { + disconnect(job.data()); + } + + mExpandGroupJobs.clear(); + mLdapSearch.startSearch(QLatin1String("*") + mSearchString); if (!collections.isEmpty()) { @@ -85,10 +178,87 @@ //the ldap query can then be used to update the name (entitydisplayattribute) for the person. } +bool PersonSearchJob::isGroup(KLDAP::LdapObject object) const +{ + const KLDAP::LdapAttrValue objectClasses = object.values(QLatin1String("objectClass")); + if (objectClasses.contains(QLatin1String("groupofuniquenames").latin1()) + || objectClasses.contains(QLatin1String("kolabgroupofuniquenames").latin1())) { + return true; + } + + return false; +} + +void PersonSearchJob::onGroupFound(QString name, QList< KLDAP::LdapObject > members) +{ + Q_ASSERT(mExpandGroupJobs.contains(name)); + ExpandGroupJob::Ptr job = mExpandGroupJobs.take(name); + Q_ASSERT(job); + + QList persons; + Q_FOREACH(const KLDAP::LdapObject &item, members) { + Person person; + person.group = name; + person.name = QString::fromUtf8(item.value(QLatin1String("cn"))); + person.mail = QString::fromUtf8(item.value(QLatin1String("mail"))); + + const int depth = item.dn().depth(); + for ( int i = 0; i < depth; ++i ) { + const QString rdnStr = item.dn().rdnString(i); + if ( rdnStr.startsWith(QLatin1String("ou="), Qt::CaseInsensitive) ) { + person.ou = rdnStr.mid(3); + break; + } + } + const QStringList &parts = person.mail.split(QLatin1Char('@')); + if (parts.count() == 2) { + const QString &uid = parts.at(0); + person.uid = uid; + if (mMatches.contains(uid)) { + const Person &p = mMatches.value(uid); + if (p.mail != person.mail ) { + if (p.rootCollection > -1) { + person.rootCollection = p.rootCollection; + person.updateDisplayName = p.updateDisplayName; + updatePersonCollection(person); + mMatches.insert(uid, person); + } else { + kWarning() << "That should not happen: we found two times persons with the same uid ("<< uid << "), but differnet name:" << p.name << "vs" << person.name; + } + } + } else { //New person found + mMatches.insert(uid, person); + persons << person; + } + } else { + kWarning() << item.dn().toString() << ": invalid email address" << person.mail; + } + } + + if (persons.count() > 0) { + emit personsFound(persons); + } + + onLDAPSearchDone(); +} + void PersonSearchJob::onLDAPSearchData(const QList< KLDAP::LdapResultObject > &list) { QList persons; Q_FOREACH(const KLDAP::LdapResultObject &item, list) { + if (isGroup(item.object)) { + ExpandGroupJob::Ptr job(new ExpandGroupJob(item.object, item.client->server(), mLdapSearch.attributes())); + connect(job.data(), SIGNAL(groupMembers(QString,QList)), SLOT(onGroupFound(QString,QList))); + + if (mExpandGroupJobs.contains(job->groupName())) { + ExpandGroupJob::Ptr j2 = mExpandGroupJobs.take(job->groupName()); + disconnect(j2.data()); + j2->deleteLater(); + } + mExpandGroupJobs.insert(job->groupName(), job); + job->start(); + continue; + } Person person; person.name = QString::fromUtf8(item.object.value(QLatin1String("cn"))); person.mail = QString::fromUtf8(item.object.value(QLatin1String("mail"))); @@ -132,6 +302,9 @@ void PersonSearchJob::onLDAPSearchDone() { + if (mExpandGroupJobs.count() > 0) { + return; + } mLdapSearchDone = true; if (mCollectionSearchDone) { emitResult(); @@ -251,3 +424,4 @@ mMatches.insert(person.uid, person); emit personUpdate(person); } +#include "personsearchjob.moc" \ No newline at end of file