Branch 'kolab/integration/4.13.0' - 4 commits - korganizer/akonadicollectionview.cpp korganizer/CMakeLists.txt korganizer/views libkdepim/CMakeLists.txt libkdepim/job

Christian Mollekopf mollekopf at kolabsys.com
Mon Jan 19 17:32:29 CET 2015


 korganizer/CMakeLists.txt                               |    2 
 korganizer/akonadicollectionview.cpp                    |    2 
 korganizer/views/collectionview/collectionsearchjob.cpp |  129 --------
 korganizer/views/collectionview/collectionsearchjob.h   |   51 ---
 korganizer/views/collectionview/controller.cpp          |   32 +-
 korganizer/views/collectionview/controller.h            |    2 
 korganizer/views/collectionview/person.h                |   46 --
 korganizer/views/collectionview/personsearchjob.cpp     |  253 ----------------
 korganizer/views/collectionview/personsearchjob.h       |   65 ----
 libkdepim/CMakeLists.txt                                |    2 
 libkdepim/job/collectionsearchjob.cpp                   |  132 ++++++++
 libkdepim/job/collectionsearchjob.h                     |   55 +++
 libkdepim/job/person.h                                  |   48 +++
 libkdepim/job/personsearchjob.cpp                       |  253 ++++++++++++++++
 libkdepim/job/personsearchjob.h                         |   67 ++++
 15 files changed, 575 insertions(+), 564 deletions(-)

New commits:
commit 3ae28f4648eb1cb9c8cdee778fd25aac0408bf25
Author: Christian Mollekopf <chrigi_1 at fastmail.fm>
Date:   Mon Jan 19 17:31:40 2015 +0100

    KOrganizer: show all personal folder when searching for *

diff --git a/korganizer/akonadicollectionview.cpp b/korganizer/akonadicollectionview.cpp
index 170fad6..2e85995 100644
--- a/korganizer/akonadicollectionview.cpp
+++ b/korganizer/akonadicollectionview.cpp
@@ -569,7 +569,7 @@ AkonadiCollectionView::AkonadiCollectionView( CalendarView *view, bool hasContex
   filterTreeViewModel->setFilterCaseSensitivity( Qt::CaseInsensitive );
 //   filterTreeViewModel->setObjectName( "Recursive filtering, for the search bar" );
   connect( searchCol, SIGNAL(textChanged(QString)),
-          filterTreeViewModel, SLOT(setFilterFixedString(QString)) );
+          filterTreeViewModel, SLOT(setFilterWildcard(QString)) );
 
   SortProxyModel *searchSortProxy = new SortProxyModel( this );
   searchSortProxy->setSourceModel(filterTreeViewModel);
diff --git a/korganizer/views/collectionview/controller.cpp b/korganizer/views/collectionview/controller.cpp
index 5bcc0e5..b89d221 100644
--- a/korganizer/views/collectionview/controller.cpp
+++ b/korganizer/views/collectionview/controller.cpp
@@ -297,43 +297,44 @@ void Controller::setSearchString(const QString &searchString)
     //TODO: Delay and abort when results are found
     mSearchModel->clear();
     emit searchIsActive(!searchString.isEmpty());
-    if (searchString.size() < 2) {
+    const bool showAllPersonalFolders = (searchString == QLatin1String("*"));
+    if (searchString.size() < 2 && !showAllPersonalFolders) {
         emit searching(false);
         return;
     }
 
-    emit searching(true);
+    if (!showAllPersonalFolders) {
+        emit searching(true);
 
-    mPersonSearchJob = new PersonSearchJob(searchString, this);
-    connect(mPersonSearchJob, SIGNAL(personsFound(QList<Person>)), this, SLOT(onPersonsFound(QList<Person>)));
-    connect(mPersonSearchJob, SIGNAL(personUpdate(Person)), this, SLOT(onPersonUpdate(Person)));
-    connect(mPersonSearchJob, SIGNAL(result(KJob*)), this, SLOT(onPersonsFound(KJob*)));
-    mPersonSearchJob->start();
+        mPersonSearchJob = new PersonSearchJob(searchString, this);
+        connect(mPersonSearchJob, SIGNAL(personsFound(QList<Person>)), this, SLOT(onPersonsFound(QList<Person>)));
+        connect(mPersonSearchJob, SIGNAL(personUpdate(Person)), this, SLOT(onPersonUpdate(Person)));
+        connect(mPersonSearchJob, SIGNAL(result(KJob*)), this, SLOT(onPersonsFound(KJob*)));
+        mPersonSearchJob->start();
+    }
 
-    mCollectionSearchJob = new CollectionSearchJob(searchString, QStringList(), this);
+    mCollectionSearchJob = new CollectionSearchJob(searchString, QStringList() << QLatin1String("text/calendar"), this);
     connect(mCollectionSearchJob, SIGNAL(result(KJob*)), this, SLOT(onCollectionsFound(KJob*)));
     mCollectionSearchJob->start();
 }
 
 void Controller::onCollectionsFound(KJob* job)
 {
+    mCollectionSearchJob = 0;
     if (!mPersonSearchJob) {
         emit searching(false);
     }
     if (job->error()) {
         kWarning() << job->errorString();
-        mCollectionSearchJob = 0;
         return;
     }
-    Q_ASSERT(mCollectionSearchJob == static_cast<CollectionSearchJob*>(job));
-    Q_FOREACH(const Akonadi::Collection &col, mCollectionSearchJob->matchingCollections()) {
+    Q_FOREACH(const Akonadi::Collection &col, static_cast<CollectionSearchJob*>(job)->matchingCollections()) {
         CollectionNode *collectionNode = new CollectionNode(*mSearchModel, col);
         collectionNode->isSearchNode = true;
         //toggled by the checkbox, results in collection getting monitored
         // connect(&collectionNode->emitter, SIGNAL(enabled(bool, Akonadi::Collection)), this, SLOT(onCollectionEnabled(bool, Akonadi::Collection)));
         mSearchModel->addNode(ReparentingModel::Node::Ptr(collectionNode));
     }
-    mCollectionSearchJob = 0;
 }
 
 void Controller::onPersonsFound(const QList<Person> &persons)
@@ -356,15 +357,14 @@ void Controller::onPersonUpdate(const Person &person)
 
 void Controller::onPersonsFound(KJob* job)
 {
+    mPersonSearchJob = 0;
     if (!mCollectionSearchJob) {
         emit searching(false);
     }
     if (job->error()) {
         kWarning() << job->errorString();
-        mPersonSearchJob = 0;
         return;
     }
-    mPersonSearchJob = 0;
 }
 
 static Akonadi::EntityTreeModel *findEtm(QAbstractItemModel *model)


commit c382227777ac9c8bfa18e3787434dc260a2be862
Author: Christian Mollekopf <chrigi_1 at fastmail.fm>
Date:   Mon Jan 19 14:00:03 2015 +0100

    Use personsearchjob and collectionsearchjob from libkdepim.

diff --git a/korganizer/CMakeLists.txt b/korganizer/CMakeLists.txt
index 07218db..5c7645e 100644
--- a/korganizer/CMakeLists.txt
+++ b/korganizer/CMakeLists.txt
@@ -175,8 +175,6 @@ set(korganizerprivate_LIB_SRCS
     akonadicollectionview.cpp
     views/collectionview/reparentingmodel.cpp
     views/collectionview/controller.cpp
-    views/collectionview/collectionsearchjob.cpp
-    views/collectionview/personsearchjob.cpp
     views/collectionview/calendardelegate.cpp
     views/collectionview/quickview.cpp
     calendarview.cpp
diff --git a/korganizer/views/collectionview/collectionsearchjob.cpp b/korganizer/views/collectionview/collectionsearchjob.cpp
deleted file mode 100644
index 0f17ab2..0000000
--- a/korganizer/views/collectionview/collectionsearchjob.cpp
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
-Copyright (C) 2014 Christian Mollekopf <mollekopf at kolabsys.com>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License along
-with this program; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-As a special exception, permission is given to link this program
-with any edition of Qt, and distribute the resulting executable,
-without including the source code for Qt in the source distribution.
-*/
-#include "collectionsearchjob.h"
-
-#include <Akonadi/CollectionFetchJob>
-#include <Akonadi/CollectionFetchScope>
-#include <baloo/pim/collectionquery.h>
-
-CollectionSearchJob::CollectionSearchJob(const QString& searchString, QObject* parent)
-    : KJob(parent),
-    mSearchString(searchString)
-{
-}
-
-void CollectionSearchJob::start()
-{
-    Baloo::PIM::CollectionQuery query;
-    //We exclude the other users namespace
-    query.setNamespace(QStringList() << QLatin1String("shared") << QLatin1String(""));
-    query.pathMatches(mSearchString);
-    query.setMimetype(QStringList() << QLatin1String("text/calendar"));
-    query.setLimit(200);
-    Baloo::PIM::ResultIterator it = query.exec();
-    Akonadi::Collection::List collections;
-    while (it.next()) {
-        collections << Akonadi::Collection(it.id());
-    }
-    kDebug() << "Found collections " << collections.size();
-    
-    if (collections.isEmpty()) {
-        //We didn't find anything
-        emitResult();
-        return;
-    }
-
-    Akonadi::CollectionFetchJob *fetchJob = new Akonadi::CollectionFetchJob(collections, Akonadi::CollectionFetchJob::Base, this);
-    fetchJob->fetchScope().setAncestorRetrieval(Akonadi::CollectionFetchScope::All);
-    fetchJob->fetchScope().setListFilter(Akonadi::CollectionFetchScope::NoFilter);
-    fetchJob->fetchScope().setIgnoreRetrievalErrors(true);
-    connect(fetchJob, SIGNAL(collectionsReceived(Akonadi::Collection::List)), this, SLOT(onCollectionsReceived(Akonadi::Collection::List)));
-    connect(fetchJob, SIGNAL(result(KJob*)), this, SLOT(onCollectionsFetched(KJob*)));
-}
-
-void CollectionSearchJob::onCollectionsReceived(const Akonadi::Collection::List &list)
-{
-    Q_FOREACH(const Akonadi::Collection &col, list) {
-        if (col.name().contains(mSearchString)) {
-            mMatchingCollections << col;
-            Akonadi::Collection ancestor = col.parentCollection();
-            while (ancestor.isValid() && (ancestor != Akonadi::Collection::root())) {
-                if (!mAncestors.contains(ancestor)) {
-                    mAncestors << ancestor;
-                }
-                ancestor = ancestor.parentCollection();
-            }
-        }
-    }
-}
-
-void CollectionSearchJob::onCollectionsFetched(KJob *job)
-{
-    if (job->error()) {
-        kWarning() << job->errorString();
-    }
-    if (!mAncestors.isEmpty()) {
-        Akonadi::CollectionFetchJob *fetchJob = new Akonadi::CollectionFetchJob(mAncestors, Akonadi::CollectionFetchJob::Base, this);
-        fetchJob->fetchScope().setListFilter(Akonadi::CollectionFetchScope::NoFilter);
-        connect(fetchJob, SIGNAL(result(KJob*)), this, SLOT(onAncestorsFetched(KJob*)));
-    } else {
-        //We didn't find anything
-        emitResult();
-    }
-}
-
-static Akonadi::Collection replaceParent(Akonadi::Collection col, const Akonadi::Collection::List &ancestors)
-{
-    if (!col.isValid()) {
-        return col;
-    }
-    const Akonadi::Collection parent = replaceParent(col.parentCollection(), ancestors);
-    Q_FOREACH (const Akonadi::Collection &c, ancestors) {
-        if (col == c) {
-            col = c;
-            break;
-        }
-    }
-    col.setParentCollection(parent);
-    return col;
-}
-
-void CollectionSearchJob::onAncestorsFetched(KJob *job)
-{
-    if (job->error()) {
-        kWarning() << job->errorString();
-    }
-    Akonadi::CollectionFetchJob *fetchJob = static_cast<Akonadi::CollectionFetchJob*>(job);
-    Akonadi::Collection::List matchingCollections;
-    Q_FOREACH (const Akonadi::Collection &c, mMatchingCollections) {
-        //We need to replace the parents with the version that contains the name, so we can display it accordingly
-        matchingCollections << replaceParent(c, fetchJob->collections());
-    }
-    mMatchingCollections = matchingCollections;
-    emitResult();
-}
-
-Akonadi::Collection::List CollectionSearchJob::matchingCollections() const
-{
-    return mMatchingCollections;
-}
-
diff --git a/korganizer/views/collectionview/collectionsearchjob.h b/korganizer/views/collectionview/collectionsearchjob.h
deleted file mode 100644
index 30b0c07..0000000
--- a/korganizer/views/collectionview/collectionsearchjob.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
-  Copyright (C) 2014 Christian Mollekopf <mollekopf at kolabsys.com>
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-  GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License along
-  with this program; if not, write to the Free Software Foundation, Inc.,
-  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-  As a special exception, permission is given to link this program
-  with any edition of Qt, and distribute the resulting executable,
-  without including the source code for Qt in the source distribution.
-*/
-
-#ifndef KORG_COLLECTIONSEARCHJOB_H
-#define KORG_COLLECTIONSEARCHJOB_H
-
-#include <KJob>
-#include <Akonadi/Collection>
-
-class CollectionSearchJob : public KJob
-{
-    Q_OBJECT
-public:
-    explicit CollectionSearchJob(const QString &searchString, QObject* parent = 0);
-
-    virtual void start();
-
-    Akonadi::Collection::List matchingCollections() const;
-
-private Q_SLOTS:
-    void onCollectionsReceived(const Akonadi::Collection::List &);
-    void onCollectionsFetched(KJob *);
-    void onAncestorsFetched(KJob *);
-
-private:
-    QString mSearchString;
-    Akonadi::Collection::List mMatchingCollections;
-    Akonadi::Collection::List mAncestors;
-};
-
-#endif
-
diff --git a/korganizer/views/collectionview/controller.cpp b/korganizer/views/collectionview/controller.cpp
index d6f294d..5bcc0e5 100644
--- a/korganizer/views/collectionview/controller.cpp
+++ b/korganizer/views/collectionview/controller.cpp
@@ -35,8 +35,8 @@
 #include <baloo/pim/collectionquery.h>
 #include <akonadi/collectionidentificationattribute.h>
 
-#include "collectionsearchjob.h"
-#include "personsearchjob.h"
+#include <libkdepim/job/collectionsearchjob.h>
+#include <libkdepim/job/personsearchjob.h>
 
 CollectionNode::CollectionNode(ReparentingModel& personModel, const Akonadi::Collection& col)
 :   Node(personModel),
@@ -310,7 +310,7 @@ void Controller::setSearchString(const QString &searchString)
     connect(mPersonSearchJob, SIGNAL(result(KJob*)), this, SLOT(onPersonsFound(KJob*)));
     mPersonSearchJob->start();
 
-    mCollectionSearchJob = new CollectionSearchJob(searchString, this);
+    mCollectionSearchJob = new CollectionSearchJob(searchString, QStringList(), this);
     connect(mCollectionSearchJob, SIGNAL(result(KJob*)), this, SLOT(onCollectionsFound(KJob*)));
     mCollectionSearchJob->start();
 }
diff --git a/korganizer/views/collectionview/controller.h b/korganizer/views/collectionview/controller.h
index e54b4ff..259ac61 100644
--- a/korganizer/views/collectionview/controller.h
+++ b/korganizer/views/collectionview/controller.h
@@ -28,7 +28,7 @@
 #include <Akonadi/EntityTreeModel>
 #include <Akonadi/Collection>
 #include "reparentingmodel.h"
-#include "person.h"
+#include <libkdepim/job/person.h>
 
 #include <libkdepim/ldap/ldapclientsearch.h>
 
diff --git a/korganizer/views/collectionview/person.h b/korganizer/views/collectionview/person.h
deleted file mode 100644
index 8fe979b..0000000
--- a/korganizer/views/collectionview/person.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-  Copyright (C) 2014 Christian Mollekopf <mollekopf at kolabsys.com>
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-  GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License along
-  with this program; if not, write to the Free Software Foundation, Inc.,
-  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-  As a special exception, permission is given to link this program
-  with any edition of Qt, and distribute the resulting executable,
-  without including the source code for Qt in the source distribution.
-*/
-
-#ifndef KORG_PERSON_H
-#define KORG_PERSON_H
-
-#include <QStringList>
-#include <Akonadi/Collection>
-
-struct Person
-{
-    Person(): rootCollection(-1), updateDisplayName(false) {};
-    QString name;
-    QString uid;
-    QString ou;
-    QString mail;
-    Akonadi::Collection::Id rootCollection;
-    bool updateDisplayName;
-    
-    //FIXME not sure we actually require those two
-    QStringList folderPaths;
-    QList<Akonadi::Collection::Id> collections;
-};
-
-Q_DECLARE_METATYPE(Person);
-
-#endif
diff --git a/korganizer/views/collectionview/personsearchjob.cpp b/korganizer/views/collectionview/personsearchjob.cpp
deleted file mode 100644
index 9fcda4e..0000000
--- a/korganizer/views/collectionview/personsearchjob.cpp
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
-  Copyright (C) 2014 Christian Mollekopf <mollekopf at kolabsys.com>
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-  GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License along
-  with this program; if not, write to the Free Software Foundation, Inc.,
-  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-  As a special exception, permission is given to link this program
-  with any edition of Qt, and distribute the resulting executable,
-  without including the source code for Qt in the source distribution.
-*/
-#include "personsearchjob.h"
-
-#include <Akonadi/EntityDisplayAttribute>
-#include <Akonadi/CollectionModifyJob>
-#include <Akonadi/CollectionFetchJob>
-#include <Akonadi/CollectionFetchScope>
-#include <KLocale>
-#include <baloo/pim/collectionquery.h>
-#include <akonadi/collectionidentificationattribute.h>
-
-PersonSearchJob::PersonSearchJob(const QString& searchString, QObject* parent)
-    : KJob(parent),
-    mSearchString(searchString)
-{
-    connect(&mLdapSearch, SIGNAL(searchData(const QList<KLDAP::LdapResultObject> &)),
-            SLOT(onLDAPSearchData(const QList<KLDAP::LdapResultObject> &)));
-
-    connect(&mLdapSearch, SIGNAL(searchDone()),
-            SLOT(onLDAPSearchDone()));
-}
-
-PersonSearchJob::~PersonSearchJob()
-{
-    mLdapSearch.cancelSearch();
-}
-
-bool PersonSearchJob::kill(KJob::KillVerbosity verbosity)
-{
-    mLdapSearch.cancelSearch();
-    return KJob::kill(verbosity);
-}
-
-void PersonSearchJob::start()
-{
-    Baloo::PIM::CollectionQuery query;
-    query.setNamespace(QStringList() << QLatin1String("usertoplevel"));
-    query.nameMatches(mSearchString);
-    query.setLimit(200);
-    Baloo::PIM::ResultIterator it = query.exec();
-    Akonadi::Collection::List collections;
-    while (it.next()) {
-        collections << Akonadi::Collection(it.id());
-    }
-    kDebug() << "Found persons " << collections.size();
-
-    mCollectionSearchDone = false;
-    mLdapSearchDone = false;
-
-    mLdapSearch.startSearch(QLatin1String("*") + mSearchString);
-
-    if (collections.isEmpty()) {
-        //We didn't find anything
-        mCollectionSearchDone = true;
-        return;
-    }
-
-    Akonadi::CollectionFetchJob *fetchJob = new Akonadi::CollectionFetchJob(collections, Akonadi::CollectionFetchJob::Base, this);
-    fetchJob->fetchScope().setAncestorRetrieval(Akonadi::CollectionFetchScope::All);
-    fetchJob->fetchScope().setListFilter(Akonadi::CollectionFetchScope::NoFilter);
-    connect(fetchJob, SIGNAL(collectionsReceived(Akonadi::Collection::List)), this, SLOT(onCollectionsReceived(Akonadi::Collection::List)));
-    connect(fetchJob, SIGNAL(result(KJob*)), this, SLOT(onCollectionsFetched(KJob*)));
-
-    //The IMAP resource should add a "Person" attribute to the collections in the person namespace,
-    //the ldap query can then be used to update the name (entitydisplayattribute) for the person.
-}
-
-void PersonSearchJob::onLDAPSearchData(const QList< KLDAP::LdapResultObject > &list)
-{
-    QList<Person> persons;
-    Q_FOREACH(const KLDAP::LdapResultObject &item, list) {
-        Person person;
-        person.name = QString::fromUtf8(item.object.value(QLatin1String("cn")));
-        person.mail = QString::fromUtf8(item.object.value(QLatin1String("mail")));
-
-        const int depth = item.object.dn().depth();
-        for ( int i = 0; i < depth; ++i ) {
-            const QString rdnStr = item.object.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.object.dn().toString() << ": invalid email address" << person.mail;
-        }
-    }
-    if (persons.count() > 0) {
-        emit personsFound(persons);
-    }
-}
-
-void PersonSearchJob::onLDAPSearchDone()
-{
-    mLdapSearchDone = true;
-    if (mCollectionSearchDone) {
-        emitResult();
-    }
-}
-
-void PersonSearchJob::onCollectionsReceived(const Akonadi::Collection::List &list)
-{
-    QList<Person> persons;
-    Q_FOREACH(const Akonadi::Collection &col, list) {
-        Person person;
-        const QString &uid = col.name();
-        const CollectionIdentificationAttribute *const attr = col.attribute<CollectionIdentificationAttribute>();
-        const Akonadi::EntityDisplayAttribute *const displayname = col.attribute<Akonadi::EntityDisplayAttribute>();
-        person.rootCollection = col.id();
-        person.uid = uid;
-        if (attr) {
-            person.ou = QString::fromUtf8(attr->ou());
-            person.mail = QString::fromUtf8(attr->mail());
-            person.name = QString::fromUtf8(attr->identifier());
-            if (!displayname || displayname->displayName().isEmpty() || displayname->displayName() == person.name) {
-                person.updateDisplayName = true;
-            }
-        } else {
-            person.name = col.displayName();
-            if (!displayname || displayname->displayName().isEmpty()) {
-                person.updateDisplayName = true;
-            }
-        }
-        if (mMatches.contains(uid)) {
-            Person p = mMatches.value(uid);
-            if (p.rootCollection > -1) {
-                //two collection with the same uid ?! 
-                kWarning() << "Two collections match to same person" << p.rootCollection << person.rootCollection;
-            } else if (p.mail != person.mail) {
-                p.rootCollection = person.rootCollection;
-                p.updateDisplayName = person.updateDisplayName;
-                updatePersonCollection(p);
-            } else {
-                mMatches.insert(uid, person);
-                emit personUpdate(person);
-            }
-        } else {
-            mMatches.insert(uid, person);
-            persons << person;
-        }
-    }
-
-    if (persons.count() > 0) {
-        emit personsFound(persons);
-    }
-}
-
-void PersonSearchJob::updatePersonCollection(const Person &person)
-{
-    Akonadi::Collection c(person.rootCollection);
-    CollectionIdentificationAttribute *identification = c.attribute<CollectionIdentificationAttribute>(Akonadi::Entity::AddIfMissing);
-
-    if (person.updateDisplayName) {
-        Akonadi::EntityDisplayAttribute *displayname  = c.attribute<Akonadi::EntityDisplayAttribute >(Akonadi::Entity::AddIfMissing);
-        displayname->setDisplayName(person.name);
-    }
-
-    //identification->setIdentifier("Other Users/" + person.uid);
-    identification->setIdentifier(person.name.toUtf8());
-    identification->setName(person.name.toUtf8());
-    identification->setCollectionNamespace("usertoplevel");
-    identification->setMail(person.mail.toUtf8());
-    identification->setOu(person.ou.toUtf8());
-
-    Akonadi::CollectionModifyJob *job = new Akonadi::CollectionModifyJob( c, this );
-    connect(job, SIGNAL(result(KJob*)), this, SLOT(modifyResult(KJob*)));
-}
-
-void PersonSearchJob::onCollectionsFetched(KJob *job)
-{
-    if (job->error()) {
-        kWarning() << job->errorString();
-    }
-    mCollectionSearchDone = true;
-    if (mLdapSearchDone) {
-        emitResult();
-    }
-}
-
-QList<Person> PersonSearchJob::matches() const
-{
-    return mMatches.values();
-}
-
-void PersonSearchJob::modifyResult(KJob *job)
-{
-    if (job->error()) {
-        kWarning() << job->errorString();
-        return;
-    }
-
-    const Akonadi::CollectionModifyJob *modifyJob = static_cast<Akonadi::CollectionModifyJob*>(job);
-    const Akonadi::Collection &col = modifyJob->collection();
-
-    const CollectionIdentificationAttribute *const attr = col.attribute<CollectionIdentificationAttribute>();
-    const Akonadi::EntityDisplayAttribute *const displayname = col.attribute<Akonadi::EntityDisplayAttribute>();
-    const QString &uid = col.name();
-    Person &person = mMatches[col.name()];
-    person.rootCollection = col.id();
-    person.uid = uid;
-    if (attr) {
-        person.ou = QString::fromUtf8(attr->ou());
-        person.mail = QString::fromUtf8(attr->mail());
-        person.name = QString::fromUtf8(attr->identifier());
-        if (!displayname || displayname->displayName().isEmpty() || displayname->displayName() == person.name) {
-            person.updateDisplayName = true;
-        }
-    }
-    kDebug() << "modified person to" << person.uid << person.name << person.rootCollection;
-
-    mMatches.insert(person.uid, person);
-    emit personUpdate(person);
-}
diff --git a/korganizer/views/collectionview/personsearchjob.h b/korganizer/views/collectionview/personsearchjob.h
deleted file mode 100644
index 43d8350..0000000
--- a/korganizer/views/collectionview/personsearchjob.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
-  Copyright (C) 2014 Christian Mollekopf <mollekopf at kolabsys.com>
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-  GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License along
-  with this program; if not, write to the Free Software Foundation, Inc.,
-  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-  As a special exception, permission is given to link this program
-  with any edition of Qt, and distribute the resulting executable,
-  without including the source code for Qt in the source distribution.
-*/
-
-#ifndef KORG_PERSONSEARCHJOB_H
-#define KORG_PERSONSEARCHJOB_H
-
-#include <KJob>
-#include <Akonadi/Collection>
-#include <libkdepim/ldap/ldapclientsearch.h>
-#include "person.h"
-
-class PersonSearchJob : public KJob
-{
-    Q_OBJECT
-public:
-    explicit PersonSearchJob(const QString &searchString, QObject* parent = 0);
-    virtual ~PersonSearchJob();
-
-    virtual void start();
-
-    QList<Person> matches() const;
-
-Q_SIGNALS:
-    void personsFound(const QList<Person> &persons);
-    void personUpdate(const Person &person);
-
-public Q_SLOTS:
-    bool kill(KillVerbosity verbosity=Quietly);
-
-private Q_SLOTS:
-    void onCollectionsReceived(const Akonadi::Collection::List &);
-    void onCollectionsFetched(KJob *);
-    void onLDAPSearchData(const QList<KLDAP::LdapResultObject> &);
-    void onLDAPSearchDone();
-    void updatePersonCollection(const Person &person);
-    void modifyResult(KJob *job);
-
-private:
-    QString mSearchString;
-    QHash<QString, Person> mMatches;
-    KLDAP::LdapClientSearch mLdapSearch;
-    bool mCollectionSearchDone;
-    bool mLdapSearchDone;
-};
-
-#endif
diff --git a/libkdepim/job/collectionsearchjob.h b/libkdepim/job/collectionsearchjob.h
index 18092fe..e23de74 100644
--- a/libkdepim/job/collectionsearchjob.h
+++ b/libkdepim/job/collectionsearchjob.h
@@ -23,11 +23,13 @@
 #ifndef KORG_COLLECTIONSEARCHJOB_H
 #define KORG_COLLECTIONSEARCHJOB_H
 
+#include "kdepim_export.h"
+
 #include <KJob>
 #include <Akonadi/Collection>
 #include <QStringList>
 
-class CollectionSearchJob : public KJob
+class KDEPIM_EXPORT CollectionSearchJob : public KJob
 {
     Q_OBJECT
 public:
diff --git a/libkdepim/job/person.h b/libkdepim/job/person.h
index 8fe979b..e11729b 100644
--- a/libkdepim/job/person.h
+++ b/libkdepim/job/person.h
@@ -23,10 +23,12 @@
 #ifndef KORG_PERSON_H
 #define KORG_PERSON_H
 
+#include "kdepim_export.h"
+
 #include <QStringList>
 #include <Akonadi/Collection>
 
-struct Person
+struct KDEPIM_EXPORT Person
 {
     Person(): rootCollection(-1), updateDisplayName(false) {};
     QString name;
diff --git a/libkdepim/job/personsearchjob.h b/libkdepim/job/personsearchjob.h
index 43d8350..8924e83 100644
--- a/libkdepim/job/personsearchjob.h
+++ b/libkdepim/job/personsearchjob.h
@@ -23,12 +23,14 @@
 #ifndef KORG_PERSONSEARCHJOB_H
 #define KORG_PERSONSEARCHJOB_H
 
+#include "kdepim_export.h"
+
 #include <KJob>
 #include <Akonadi/Collection>
 #include <libkdepim/ldap/ldapclientsearch.h>
 #include "person.h"
 
-class PersonSearchJob : public KJob
+class KDEPIM_EXPORT PersonSearchJob : public KJob
 {
     Q_OBJECT
 public:


commit 720ea7a504dbf097312fb75fa67313ee321bf15d
Author: Christian Mollekopf <chrigi_1 at fastmail.fm>
Date:   Fri Jan 16 18:40:53 2015 +0100

    Re-importet modified personsearchjob from zanshin.

diff --git a/libkdepim/CMakeLists.txt b/libkdepim/CMakeLists.txt
index b51d976..17bda53 100644
--- a/libkdepim/CMakeLists.txt
+++ b/libkdepim/CMakeLists.txt
@@ -39,6 +39,7 @@ set(kdepim_LIB_SRCS
    job/openemailaddressjob.cpp
    job/addemaildisplayjob.cpp
    job/collectionsearchjob.cpp
+   job/personsearchjob.cpp
    addressline/completionordereditor.cpp
    addressline/addresseelineedit.cpp
    addressline/recentaddresses.cpp
diff --git a/libkdepim/job/person.h b/libkdepim/job/person.h
new file mode 100644
index 0000000..8fe979b
--- /dev/null
+++ b/libkdepim/job/person.h
@@ -0,0 +1,46 @@
+/*
+  Copyright (C) 2014 Christian Mollekopf <mollekopf at kolabsys.com>
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along
+  with this program; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+  As a special exception, permission is given to link this program
+  with any edition of Qt, and distribute the resulting executable,
+  without including the source code for Qt in the source distribution.
+*/
+
+#ifndef KORG_PERSON_H
+#define KORG_PERSON_H
+
+#include <QStringList>
+#include <Akonadi/Collection>
+
+struct Person
+{
+    Person(): rootCollection(-1), updateDisplayName(false) {};
+    QString name;
+    QString uid;
+    QString ou;
+    QString mail;
+    Akonadi::Collection::Id rootCollection;
+    bool updateDisplayName;
+    
+    //FIXME not sure we actually require those two
+    QStringList folderPaths;
+    QList<Akonadi::Collection::Id> collections;
+};
+
+Q_DECLARE_METATYPE(Person);
+
+#endif
diff --git a/libkdepim/job/personsearchjob.cpp b/libkdepim/job/personsearchjob.cpp
new file mode 100644
index 0000000..39838a2
--- /dev/null
+++ b/libkdepim/job/personsearchjob.cpp
@@ -0,0 +1,253 @@
+/*
+  Copyright (C) 2014 Christian Mollekopf <mollekopf at kolabsys.com>
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along
+  with this program; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+  As a special exception, permission is given to link this program
+  with any edition of Qt, and distribute the resulting executable,
+  without including the source code for Qt in the source distribution.
+*/
+#include "personsearchjob.h"
+
+#include <Akonadi/EntityDisplayAttribute>
+#include <Akonadi/CollectionModifyJob>
+#include <Akonadi/CollectionFetchJob>
+#include <Akonadi/CollectionFetchScope>
+#include <KLocale>
+#include <baloo/pim/collectionquery.h>
+#include <akonadi/collectionidentificationattribute.h>
+
+PersonSearchJob::PersonSearchJob(const QString& searchString, QObject* parent)
+    : KJob(parent),
+    mSearchString(searchString)
+{
+    connect(&mLdapSearch, SIGNAL(searchData(const QList<KLDAP::LdapResultObject> &)),
+            SLOT(onLDAPSearchData(const QList<KLDAP::LdapResultObject> &)));
+
+    connect(&mLdapSearch, SIGNAL(searchDone()),
+            SLOT(onLDAPSearchDone()));
+}
+
+PersonSearchJob::~PersonSearchJob()
+{
+    mLdapSearch.cancelSearch();
+}
+
+bool PersonSearchJob::kill(KJob::KillVerbosity verbosity)
+{
+    mLdapSearch.cancelSearch();
+    return KJob::kill(verbosity);
+}
+
+void PersonSearchJob::start()
+{
+    Baloo::PIM::CollectionQuery query;
+    query.setNamespace(QStringList() << QLatin1String("usertoplevel"));
+    query.nameMatches(mSearchString);
+    query.setLimit(200);
+    Baloo::PIM::ResultIterator it = query.exec();
+    Akonadi::Collection::List collections;
+    while (it.next()) {
+        collections << Akonadi::Collection(it.id());
+    }
+    kDebug() << "Found persons " << collections.size();
+
+    mCollectionSearchDone = false;
+    mLdapSearchDone = false;
+    if (collections.isEmpty()) {
+        //We didn't find anything
+        mCollectionSearchDone = true;
+    }
+
+    mLdapSearch.startSearch(QLatin1String("*") + mSearchString);
+
+    if (!collections.isEmpty()) {
+        Akonadi::CollectionFetchJob *fetchJob = new Akonadi::CollectionFetchJob(collections, Akonadi::CollectionFetchJob::Base, this);
+        fetchJob->fetchScope().setAncestorRetrieval(Akonadi::CollectionFetchScope::All);
+        fetchJob->fetchScope().setListFilter(Akonadi::CollectionFetchScope::NoFilter);
+        connect(fetchJob, SIGNAL(collectionsReceived(Akonadi::Collection::List)), this, SLOT(onCollectionsReceived(Akonadi::Collection::List)));
+        connect(fetchJob, SIGNAL(result(KJob*)), this, SLOT(onCollectionsFetched(KJob*)));
+    }
+
+    //The IMAP resource should add a "Person" attribute to the collections in the person namespace,
+    //the ldap query can then be used to update the name (entitydisplayattribute) for the person.
+}
+
+void PersonSearchJob::onLDAPSearchData(const QList< KLDAP::LdapResultObject > &list)
+{
+    QList<Person> persons;
+    Q_FOREACH(const KLDAP::LdapResultObject &item, list) {
+        Person person;
+        person.name = QString::fromUtf8(item.object.value(QLatin1String("cn")));
+        person.mail = QString::fromUtf8(item.object.value(QLatin1String("mail")));
+
+        const int depth = item.object.dn().depth();
+        for ( int i = 0; i < depth; ++i ) {
+            const QString rdnStr = item.object.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.object.dn().toString() << ": invalid email address" << person.mail;
+        }
+    }
+    if (persons.count() > 0) {
+        emit personsFound(persons);
+    }
+}
+
+void PersonSearchJob::onLDAPSearchDone()
+{
+    mLdapSearchDone = true;
+    if (mCollectionSearchDone) {
+        emitResult();
+    }
+}
+
+void PersonSearchJob::onCollectionsReceived(const Akonadi::Collection::List &list)
+{
+    QList<Person> persons;
+    Q_FOREACH(const Akonadi::Collection &col, list) {
+        Person person;
+        const QString &uid = col.name();
+        const CollectionIdentificationAttribute *const attr = col.attribute<CollectionIdentificationAttribute>();
+        const Akonadi::EntityDisplayAttribute *const displayname = col.attribute<Akonadi::EntityDisplayAttribute>();
+        person.rootCollection = col.id();
+        person.uid = uid;
+        if (attr) {
+            person.ou = QString::fromUtf8(attr->ou());
+            person.mail = QString::fromUtf8(attr->mail());
+            person.name = QString::fromUtf8(attr->identifier());
+            if (!displayname || displayname->displayName().isEmpty() || displayname->displayName() == person.name) {
+                person.updateDisplayName = true;
+            }
+        } else {
+            person.name = col.displayName();
+            if (!displayname || displayname->displayName().isEmpty()) {
+                person.updateDisplayName = true;
+            }
+        }
+        if (mMatches.contains(uid)) {
+            Person p = mMatches.value(uid);
+            if (p.rootCollection > -1) {
+                //two collection with the same uid ?! 
+                kWarning() << "Two collections match to same person" << p.rootCollection << person.rootCollection;
+            } else if (p.mail != person.mail) {
+                p.rootCollection = person.rootCollection;
+                p.updateDisplayName = person.updateDisplayName;
+                updatePersonCollection(p);
+            } else {
+                mMatches.insert(uid, person);
+                emit personUpdate(person);
+            }
+        } else {
+            mMatches.insert(uid, person);
+            persons << person;
+        }
+    }
+
+    if (persons.count() > 0) {
+        emit personsFound(persons);
+    }
+}
+
+void PersonSearchJob::updatePersonCollection(const Person &person)
+{
+    Akonadi::Collection c(person.rootCollection);
+    CollectionIdentificationAttribute *identification = c.attribute<CollectionIdentificationAttribute>(Akonadi::Entity::AddIfMissing);
+
+    if (person.updateDisplayName) {
+        Akonadi::EntityDisplayAttribute *displayname  = c.attribute<Akonadi::EntityDisplayAttribute >(Akonadi::Entity::AddIfMissing);
+        displayname->setDisplayName(person.name);
+    }
+
+    //identification->setIdentifier("Other Users/" + person.uid);
+    identification->setIdentifier(person.name.toUtf8());
+    identification->setName(person.name.toUtf8());
+    identification->setCollectionNamespace("usertoplevel");
+    identification->setMail(person.mail.toUtf8());
+    identification->setOu(person.ou.toUtf8());
+
+    Akonadi::CollectionModifyJob *job = new Akonadi::CollectionModifyJob( c, this );
+    connect(job, SIGNAL(result(KJob*)), this, SLOT(modifyResult(KJob*)));
+}
+
+void PersonSearchJob::onCollectionsFetched(KJob *job)
+{
+    if (job->error()) {
+        kWarning() << job->errorString();
+    }
+    mCollectionSearchDone = true;
+    if (mLdapSearchDone) {
+        emitResult();
+    }
+}
+
+QList<Person> PersonSearchJob::matches() const
+{
+    return mMatches.values();
+}
+
+void PersonSearchJob::modifyResult(KJob *job)
+{
+    if (job->error()) {
+        kWarning() << job->errorString();
+        return;
+    }
+
+    const Akonadi::CollectionModifyJob *modifyJob = static_cast<Akonadi::CollectionModifyJob*>(job);
+    const Akonadi::Collection &col = modifyJob->collection();
+
+    const CollectionIdentificationAttribute *const attr = col.attribute<CollectionIdentificationAttribute>();
+    const Akonadi::EntityDisplayAttribute *const displayname = col.attribute<Akonadi::EntityDisplayAttribute>();
+    const QString &uid = col.name();
+    Person &person = mMatches[col.name()];
+    person.rootCollection = col.id();
+    person.uid = uid;
+    if (attr) {
+        person.ou = QString::fromUtf8(attr->ou());
+        person.mail = QString::fromUtf8(attr->mail());
+        person.name = QString::fromUtf8(attr->identifier());
+        if (!displayname || displayname->displayName().isEmpty() || displayname->displayName() == person.name) {
+            person.updateDisplayName = true;
+        }
+    }
+    kDebug() << "modified person to" << person.uid << person.name << person.rootCollection;
+
+    mMatches.insert(person.uid, person);
+    emit personUpdate(person);
+}
diff --git a/libkdepim/job/personsearchjob.h b/libkdepim/job/personsearchjob.h
new file mode 100644
index 0000000..43d8350
--- /dev/null
+++ b/libkdepim/job/personsearchjob.h
@@ -0,0 +1,65 @@
+/*
+  Copyright (C) 2014 Christian Mollekopf <mollekopf at kolabsys.com>
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along
+  with this program; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+  As a special exception, permission is given to link this program
+  with any edition of Qt, and distribute the resulting executable,
+  without including the source code for Qt in the source distribution.
+*/
+
+#ifndef KORG_PERSONSEARCHJOB_H
+#define KORG_PERSONSEARCHJOB_H
+
+#include <KJob>
+#include <Akonadi/Collection>
+#include <libkdepim/ldap/ldapclientsearch.h>
+#include "person.h"
+
+class PersonSearchJob : public KJob
+{
+    Q_OBJECT
+public:
+    explicit PersonSearchJob(const QString &searchString, QObject* parent = 0);
+    virtual ~PersonSearchJob();
+
+    virtual void start();
+
+    QList<Person> matches() const;
+
+Q_SIGNALS:
+    void personsFound(const QList<Person> &persons);
+    void personUpdate(const Person &person);
+
+public Q_SLOTS:
+    bool kill(KillVerbosity verbosity=Quietly);
+
+private Q_SLOTS:
+    void onCollectionsReceived(const Akonadi::Collection::List &);
+    void onCollectionsFetched(KJob *);
+    void onLDAPSearchData(const QList<KLDAP::LdapResultObject> &);
+    void onLDAPSearchDone();
+    void updatePersonCollection(const Person &person);
+    void modifyResult(KJob *job);
+
+private:
+    QString mSearchString;
+    QHash<QString, Person> mMatches;
+    KLDAP::LdapClientSearch mLdapSearch;
+    bool mCollectionSearchDone;
+    bool mLdapSearchDone;
+};
+
+#endif


commit 30e818f9f6eddb3822f62f1aafabbd39029f1a51
Author: Christian Mollekopf <chrigi_1 at fastmail.fm>
Date:   Fri Jan 16 18:32:25 2015 +0100

    Re-imported modified collectionsearchjob from zanshin to be shared.

diff --git a/libkdepim/CMakeLists.txt b/libkdepim/CMakeLists.txt
index f8b1725..b51d976 100644
--- a/libkdepim/CMakeLists.txt
+++ b/libkdepim/CMakeLists.txt
@@ -38,6 +38,7 @@ set(kdepim_LIB_SRCS
    job/addcontactjob.cpp
    job/openemailaddressjob.cpp
    job/addemaildisplayjob.cpp
+   job/collectionsearchjob.cpp
    addressline/completionordereditor.cpp
    addressline/addresseelineedit.cpp
    addressline/recentaddresses.cpp
diff --git a/libkdepim/job/collectionsearchjob.cpp b/libkdepim/job/collectionsearchjob.cpp
new file mode 100644
index 0000000..066bcff
--- /dev/null
+++ b/libkdepim/job/collectionsearchjob.cpp
@@ -0,0 +1,132 @@
+/*
+Copyright (C) 2014 Christian Mollekopf <mollekopf at kolabsys.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+As a special exception, permission is given to link this program
+with any edition of Qt, and distribute the resulting executable,
+without including the source code for Qt in the source distribution.
+*/
+#include "collectionsearchjob.h"
+
+#include <Akonadi/CollectionFetchJob>
+#include <Akonadi/CollectionFetchScope>
+#include <baloo/pim/collectionquery.h>
+
+CollectionSearchJob::CollectionSearchJob(const QString& searchString, const QStringList &mimetypeFilter, QObject* parent)
+    : KJob(parent),
+    mSearchString(searchString),
+    mMimeTypeFilter(mimetypeFilter)
+{
+}
+
+void CollectionSearchJob::start()
+{
+    Baloo::PIM::CollectionQuery query;
+    if (mSearchString == QLatin1String("*")) {
+        query.setNamespace(QStringList() << QLatin1String(""));
+    } else {
+        //We exclude the other users namespace
+        query.setNamespace(QStringList() << QLatin1String("shared") << QLatin1String(""));
+        query.pathMatches(mSearchString);
+    }
+    query.setMimetype(mMimeTypeFilter);
+    query.setLimit(200);
+    Baloo::PIM::ResultIterator it = query.exec();
+    Akonadi::Collection::List collections;
+    while (it.next()) {
+        collections << Akonadi::Collection(it.id());
+    }
+    kDebug() << "Found collections " << collections.size();
+    
+    if (collections.isEmpty()) {
+        //We didn't find anything
+        emitResult();
+        return;
+    }
+
+    Akonadi::CollectionFetchJob *fetchJob = new Akonadi::CollectionFetchJob(collections, Akonadi::CollectionFetchJob::Base, this);
+    fetchJob->fetchScope().setAncestorRetrieval(Akonadi::CollectionFetchScope::All);
+    fetchJob->fetchScope().setListFilter(Akonadi::CollectionFetchScope::NoFilter);
+    fetchJob->fetchScope().setIgnoreRetrievalErrors(true);
+    connect(fetchJob, SIGNAL(collectionsReceived(Akonadi::Collection::List)), this, SLOT(onCollectionsReceived(Akonadi::Collection::List)));
+    connect(fetchJob, SIGNAL(result(KJob*)), this, SLOT(onCollectionsFetched(KJob*)));
+}
+
+void CollectionSearchJob::onCollectionsReceived(const Akonadi::Collection::List &list)
+{
+    Q_FOREACH(const Akonadi::Collection &col, list) {
+        mMatchingCollections << col;
+        Akonadi::Collection ancestor = col.parentCollection();
+        while (ancestor.isValid() && (ancestor != Akonadi::Collection::root())) {
+            if (!mAncestors.contains(ancestor)) {
+                mAncestors << ancestor;
+            }
+            ancestor = ancestor.parentCollection();
+        }
+    }
+}
+
+void CollectionSearchJob::onCollectionsFetched(KJob *job)
+{
+    if (job->error()) {
+        kWarning() << job->errorString();
+    }
+    if (!mAncestors.isEmpty()) {
+        Akonadi::CollectionFetchJob *fetchJob = new Akonadi::CollectionFetchJob(mAncestors, Akonadi::CollectionFetchJob::Base, this);
+        fetchJob->fetchScope().setListFilter(Akonadi::CollectionFetchScope::NoFilter);
+        connect(fetchJob, SIGNAL(result(KJob*)), this, SLOT(onAncestorsFetched(KJob*)));
+    } else {
+        //We didn't find anything
+        emitResult();
+    }
+}
+
+static Akonadi::Collection replaceParent(Akonadi::Collection col, const Akonadi::Collection::List &ancestors)
+{
+    if (!col.isValid()) {
+        return col;
+    }
+    const Akonadi::Collection parent = replaceParent(col.parentCollection(), ancestors);
+    Q_FOREACH (const Akonadi::Collection &c, ancestors) {
+        if (col == c) {
+            col = c;
+            break;
+        }
+    }
+    col.setParentCollection(parent);
+    return col;
+}
+
+void CollectionSearchJob::onAncestorsFetched(KJob *job)
+{
+    if (job->error()) {
+        kWarning() << job->errorString();
+    }
+    Akonadi::CollectionFetchJob *fetchJob = static_cast<Akonadi::CollectionFetchJob*>(job);
+    Akonadi::Collection::List matchingCollections;
+    Q_FOREACH (const Akonadi::Collection &c, mMatchingCollections) {
+        //We need to replace the parents with the version that contains the name, so we can display it accordingly
+        matchingCollections << replaceParent(c, fetchJob->collections());
+    }
+    mMatchingCollections = matchingCollections;
+    emitResult();
+}
+
+Akonadi::Collection::List CollectionSearchJob::matchingCollections() const
+{
+    return mMatchingCollections;
+}
+
diff --git a/libkdepim/job/collectionsearchjob.h b/libkdepim/job/collectionsearchjob.h
new file mode 100644
index 0000000..18092fe
--- /dev/null
+++ b/libkdepim/job/collectionsearchjob.h
@@ -0,0 +1,53 @@
+/*
+  Copyright (C) 2014 Christian Mollekopf <mollekopf at kolabsys.com>
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along
+  with this program; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+  As a special exception, permission is given to link this program
+  with any edition of Qt, and distribute the resulting executable,
+  without including the source code for Qt in the source distribution.
+*/
+
+#ifndef KORG_COLLECTIONSEARCHJOB_H
+#define KORG_COLLECTIONSEARCHJOB_H
+
+#include <KJob>
+#include <Akonadi/Collection>
+#include <QStringList>
+
+class CollectionSearchJob : public KJob
+{
+    Q_OBJECT
+public:
+    explicit CollectionSearchJob(const QString &searchString, const QStringList &mimetypeFilter, QObject* parent = 0);
+
+    virtual void start();
+
+    Akonadi::Collection::List matchingCollections() const;
+
+private Q_SLOTS:
+    void onCollectionsReceived(const Akonadi::Collection::List &);
+    void onCollectionsFetched(KJob *);
+    void onAncestorsFetched(KJob *);
+
+private:
+    QString mSearchString;
+    QStringList mMimeTypeFilter;
+    Akonadi::Collection::List mMatchingCollections;
+    Akonadi::Collection::List mAncestors;
+};
+
+#endif
+




More information about the commits mailing list