Branch 'kolab/integration/4.13.0' - 6 commits - akonadi/calendar kcalutils/incidenceformatter.cpp kpimidentities/CMakeLists.txt kpimidentities/utils.cpp kpimidentities/utils.h

Sandro Knauß knauss at kolabsys.com
Wed Sep 24 21:16:20 CEST 2014


 akonadi/calendar/CMakeLists.txt                      |    1 
 akonadi/calendar/calendarbase.cpp                    |   45 ++++++
 akonadi/calendar/calendarbase_p.h                    |    7 +
 akonadi/calendar/calfilterpartstatusproxymodel_p.cpp |  124 +++++++++++++++++++
 akonadi/calendar/calfilterpartstatusproxymodel_p.h   |   58 ++++++++
 akonadi/calendar/etmcalendar.cpp                     |   14 +-
 akonadi/calendar/etmcalendar_p.h                     |    3 
 akonadi/calendar/fetchjobcalendar.cpp                |   26 ++-
 akonadi/calendar/fetchjobcalendar_p.h                |    3 
 akonadi/calendar/itiphandler.cpp                     |    3 
 akonadi/calendar/scheduler_p.cpp                     |    4 
 akonadi/calendar/tests/etmcalendartest.cpp           |   70 ++++++++++
 akonadi/calendar/tests/etmcalendartest.h             |    2 
 akonadi/calendar/tests/itiphandlertest.cpp           |   12 +
 akonadi/calendar/utils_p.cpp                         |   51 +------
 akonadi/calendar/utils_p.h                           |    4 
 kcalutils/incidenceformatter.cpp                     |   70 +++-------
 kpimidentities/CMakeLists.txt                        |    3 
 kpimidentities/utils.cpp                             |   69 ++++++++++
 kpimidentities/utils.h                               |   45 ++++++
 20 files changed, 513 insertions(+), 101 deletions(-)

New commits:
commit fd0b82b0813fc2b5ea8bad6b0851cc0c7797b699
Author: Sandro Knauß <knauss at kolabsys.com>
Date:   Thu Sep 4 15:22:02 2014 +0200

    Filter out new invitatins and declined events from etmcalendar.
    
    invitations and declined events are available over a search collection,
    so we don't want to show them in normal calendars.
    
    Kolab: #3549

diff --git a/akonadi/calendar/CMakeLists.txt b/akonadi/calendar/CMakeLists.txt
index 951c700..3e68aa9 100644
--- a/akonadi/calendar/CMakeLists.txt
+++ b/akonadi/calendar/CMakeLists.txt
@@ -22,6 +22,7 @@ set(akonadicalendar_LIB_SRC
   calendarclipboard.cpp
   calendarmodel.cpp
   calfilterproxymodel_p.cpp
+  calfilterpartstatusproxymodel_p.cpp
   etmcalendar.cpp
   history.cpp
   history_p.cpp
diff --git a/akonadi/calendar/calfilterpartstatusproxymodel_p.cpp b/akonadi/calendar/calfilterpartstatusproxymodel_p.cpp
new file mode 100644
index 0000000..648f9a2
--- /dev/null
+++ b/akonadi/calendar/calfilterpartstatusproxymodel_p.cpp
@@ -0,0 +1,124 @@
+/*
+  Copyright (c) 2014 Sandro Knauß <knauss at kolabsys.com>
+
+  This library is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Library General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or (at your
+  option) any later version.
+
+  This library 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 Library General Public
+  License for more details.
+
+  You should have received a copy of the GNU Library General Public License
+  along with this library; see the file COPYING.LIB.  If not, write to the
+  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include "calfilterpartstatusproxymodel_p.h"
+#include "utils_p.h"
+
+#include <akonadi/collection.h>
+#include <akonadi/item.h>
+#include <akonadi/entitytreemodel.h>
+
+#include <kcalcore/incidence.h>
+#include <kcalcore/attendee.h>
+#include <kpimutils/email.h>
+
+#include <KPIMIdentities/IdentityManager>
+#include <KEMailSettings>
+
+using namespace Akonadi;
+
+class CalFilterPartStatusProxyModel::Private
+{
+public:
+    explicit Private()
+        : mFilterVirtual(false)
+        , mIdentityManager(/*ro=*/ true)
+    {
+    }
+
+    bool mFilterVirtual;
+    QList<KCalCore::Attendee::PartStat> mBlockedStatusList;
+    KPIMIdentities::IdentityManager mIdentityManager;
+};
+
+void CalFilterPartStatusProxyModel::slotIdentitiesChanged()
+{
+    emit invalidate();
+}
+
+CalFilterPartStatusProxyModel::CalFilterPartStatusProxyModel(QObject *parent)
+    : QSortFilterProxyModel(parent)
+    , d(new Private())
+{
+    connect(&(d->mIdentityManager), SIGNAL(changed()),
+            SLOT(slotIdentitiesChanged()));
+}
+
+CalFilterPartStatusProxyModel::~CalFilterPartStatusProxyModel()
+{
+    delete d;
+}
+
+const QList<KCalCore::Attendee::PartStat> &CalFilterPartStatusProxyModel::blockedStatusList() const
+{
+    return d->mBlockedStatusList;
+}
+
+void CalFilterPartStatusProxyModel::setBlockedStatusList(const QList<KCalCore::Attendee::PartStat> &blockStatusList)
+{
+    d->mBlockedStatusList = blockStatusList;
+}
+
+
+bool CalFilterPartStatusProxyModel::filterVirtual() const
+{
+    return d->mFilterVirtual;
+}
+
+void CalFilterPartStatusProxyModel::setFilterVirtual(bool filterVirtual)
+{
+    d->mFilterVirtual = filterVirtual;
+}
+
+
+bool CalFilterPartStatusProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
+{
+    const QModelIndex idx = sourceModel()->index(source_row, 0, source_parent);
+    if (!idx.isValid())
+        return false;
+
+    const Akonadi::Item item = idx.data(Akonadi::EntityTreeModel::ItemRole).value<Akonadi::Item>();
+    if (!item.isValid() || !item.hasPayload<KCalCore::Incidence::Ptr>()) {
+        return false;
+    }
+
+    const KCalCore::Incidence::Ptr incidence = item.payload<KCalCore::Incidence::Ptr>();
+    if (!incidence) {
+        return false;
+    }
+
+    // Incidences from virtual collections are always ok
+    const Akonadi::Collection col = idx.data(Akonadi::EntityTreeModel::ParentCollectionRole).value<Akonadi::Collection>();
+    if (!d->mFilterVirtual && col.isVirtual()) {
+        return true;
+    }
+
+    foreach (const KCalCore::Attendee::Ptr &attendee, incidence->attendees()) {
+        if ( CalendarUtils::thatIsMe(attendee) ) {
+            if ( d->mBlockedStatusList.contains(attendee->status()) ) {
+                return false;
+            } else {
+                return true;
+            }
+        }
+    }
+
+    // We are not attendee, so we accept the incidence
+    return true;
+}
diff --git a/akonadi/calendar/calfilterpartstatusproxymodel_p.h b/akonadi/calendar/calfilterpartstatusproxymodel_p.h
new file mode 100644
index 0000000..a12ccb7
--- /dev/null
+++ b/akonadi/calendar/calfilterpartstatusproxymodel_p.h
@@ -0,0 +1,58 @@
+/*
+  Copyright (c) 2014 Sandro Knauß <knauss at kolabsys.com>
+
+  This library is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Library General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or (at your
+  option) any later version.
+
+  This library 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 Library General Public
+  License for more details.
+
+  You should have received a copy of the GNU Library General Public License
+  along with this library; see the file COPYING.LIB.  If not, write to the
+  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#ifndef AKONADI_CALFILTERPARTSTATUSPROXYMODEL_P_H
+#define AKONADI_CALFILTERPARTSTATUSPROXYMODEL_P_H
+
+#include <QSortFilterProxyModel>
+#include <kcalcore/attendee.h>
+
+namespace Akonadi {
+
+class CalFilterPartStatusProxyModel : public QSortFilterProxyModel
+{
+    Q_OBJECT
+public:
+    explicit CalFilterPartStatusProxyModel(QObject *parent=0);
+    ~CalFilterPartStatusProxyModel();
+
+    void setFilterVirtual(bool filterVirtual);
+    bool filterVirtual() const;
+
+    void setBlockedStatusList(const QList<KCalCore::Attendee::PartStat> &blockStatusList);
+    const QList<KCalCore::Attendee::PartStat> &blockedStatusList() const;
+
+protected:
+    /* reimp */
+    bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const;
+
+private:
+    //@cond PRIVATE
+    class Private;
+    Private *const d;
+
+    //Q_PRIVATE_SLOT(d, void slotIdentitiesChanged())
+    //@endcond
+private slots:
+    void slotIdentitiesChanged();
+};
+
+}
+
+#endif
diff --git a/akonadi/calendar/etmcalendar.cpp b/akonadi/calendar/etmcalendar.cpp
index 194091c..ff051c3 100644
--- a/akonadi/calendar/etmcalendar.cpp
+++ b/akonadi/calendar/etmcalendar.cpp
@@ -23,6 +23,7 @@
 #include "calendarmodel_p.h"
 #include "kcolumnfilterproxymodel_p.h"
 #include "calfilterproxymodel_p.h"
+#include "calfilterpartstatusproxymodel_p.h"
 #include "utils_p.h"
 
 #include <akonadi/item.h>
@@ -66,6 +67,7 @@ ETMCalendarPrivate::ETMCalendarPrivate(ETMCalendar *qq) : CalendarBasePrivate(qq
     , mCheckableProxyModel(0)
     , mCollectionProxyModel(0)
     , mCalFilterProxyModel(0)
+    , mCalFilterPartStatusProxyModel(0)
     , mSelectionProxy(0)
     , mCollectionFilteringEnabled(true)
     , q(qq)
@@ -185,8 +187,18 @@ void ETMCalendarPrivate::setupFilteredETM()
     mCalFilterProxyModel->setSourceModel(mSelectionProxy);
     mCalFilterProxyModel->setObjectName("KCalCore::CalFilter filtering");
 
+    mCalFilterPartStatusProxyModel = new CalFilterPartStatusProxyModel(this);
+    mCalFilterPartStatusProxyModel->setFilterVirtual(false);
+    QList<KCalCore::Attendee::PartStat> blockedStatusList;
+    blockedStatusList << KCalCore::Attendee::NeedsAction;
+    blockedStatusList << KCalCore::Attendee::Declined;
+    mCalFilterPartStatusProxyModel->setDynamicSortFilter(true);
+    mCalFilterPartStatusProxyModel->setBlockedStatusList(blockedStatusList);
+    mCalFilterPartStatusProxyModel->setSourceModel(mCalFilterProxyModel);
+    mCalFilterPartStatusProxyModel->setObjectName("PartStatus filtering");
+
     mFilteredETM = new Akonadi::EntityMimeTypeFilterModel(this);
-    mFilteredETM->setSourceModel(mCalFilterProxyModel);
+    mFilteredETM->setSourceModel(mCalFilterPartStatusProxyModel);
     mFilteredETM->setHeaderGroup(Akonadi::EntityTreeModel::ItemListHeaders);
     mFilteredETM->setSortRole(CalendarModel::SortRole);
     mFilteredETM->setObjectName("Show headers");
diff --git a/akonadi/calendar/etmcalendar_p.h b/akonadi/calendar/etmcalendar_p.h
index ec57722..c998c01 100644
--- a/akonadi/calendar/etmcalendar_p.h
+++ b/akonadi/calendar/etmcalendar_p.h
@@ -41,6 +41,7 @@ class EntityTreeModel;
 class EntityMimeTypeFilterModel;
 class CollectionFilterProxyModel;
 class CalFilterProxyModel;
+class CalFilterPartStatusProxyModel;
 
 static bool isStructuralCollection(const Akonadi::Collection &collection)
 {
@@ -133,6 +134,8 @@ public:
     CheckableProxyModel *mCheckableProxyModel;
     Akonadi::CollectionFilterProxyModel *mCollectionProxyModel;
     Akonadi::CalFilterProxyModel *mCalFilterProxyModel; //KCalCore::CalFilter stuff
+    //filter out all invitations and declined events
+    Akonadi::CalFilterPartStatusProxyModel *mCalFilterPartStatusProxyModel;
     KSelectionProxyModel *mSelectionProxy;
     bool mCollectionFilteringEnabled;
     QSet<Akonadi::Collection::Id> mPopulatedCollectionIds;
diff --git a/akonadi/calendar/tests/etmcalendartest.cpp b/akonadi/calendar/tests/etmcalendartest.cpp
index f093363..c45684e 100644
--- a/akonadi/calendar/tests/etmcalendartest.cpp
+++ b/akonadi/calendar/tests/etmcalendartest.cpp
@@ -30,6 +30,7 @@
 #include <akonadi/itemdeletejob.h>
 #include <akonadi/itemmodifyjob.h>
 #include <KCheckableProxyModel>
+#include <KEMailSettings>
 
 #include <QTestEventLoop>
 #include <QSignalSpy>
@@ -532,6 +533,75 @@ void ETMCalendarTest::testShareETM()
     QVERIFY(calendar2->incidences().isEmpty());
 }
 
+void ETMCalendarTest::testFilterInvitations()
+{
+    int anz = mCalendar->model()->rowCount();
+    QString uid = QLatin1String("invite-01");
+    Item item;
+    Incidence::Ptr incidence = Incidence::Ptr(new Event());
+    KEMailSettings emailSettings;
+    KCalCore::Attendee::Ptr me(new KCalCore::Attendee(QLatin1String("me"), emailSettings.getSetting(KEMailSettings::EmailAddress)));
+
+    item.setMimeType(Event::eventMimeType());
+    incidence->setUid(uid);
+    incidence->setDtStart(KDateTime::currentDateTime(KDateTime::UTC));
+    incidence->setSummary(QLatin1String("summary"));
+
+    me->setStatus(KCalCore::Attendee::NeedsAction);
+    incidence->addAttendee(me);
+
+    item.setPayload<KCalCore::Incidence::Ptr>(incidence);
+    ItemCreateJob *job = new ItemCreateJob(item, mCollection, this);
+    AKVERIFYEXEC(job);
+    waitForIt();
+    // incidence do not pop up in model
+    QCOMPARE(mCalendar->model()->rowCount(), anz);
+
+    kDebug() << "first invite ended";
+}
+
+void ETMCalendarTest::testFilterInvitationsChanged()
+{
+    int anz = mCalendar->model()->rowCount();
+
+    KEMailSettings emailSettings;
+    KCalCore::Attendee::Ptr me(new KCalCore::Attendee(QLatin1String("me"), emailSettings.getSetting(KEMailSettings::EmailAddress)));
+
+    QString uid = QLatin1String("invite-02");
+    mIncidencesToAdd = 1;
+    createIncidence(uid);
+    waitForIt();
+    QCOMPARE(mCalendar->model()->rowCount(), anz+1);
+
+    Incidence::Ptr incidence = mCalendar->incidence(uid);
+    Item item = mCalendar->item(uid);
+
+    incidence->addAttendee(me);
+    incidence->setRevision(1);
+    item.setPayload<KCalCore::Incidence::Ptr>(incidence);
+
+    mIncidencesToDelete = 1;
+    ItemModifyJob *modifyJob = new ItemModifyJob(item,  this);
+    AKVERIFYEXEC(modifyJob);
+    waitForIt();
+    QCOMPARE(mCalendar->model()->rowCount(), anz);
+
+    me->setStatus(KCalCore::Attendee::Accepted);
+    incidence->clearAttendees();
+    incidence->addAttendee(me);
+
+    incidence->setRevision(2);
+
+    item.setPayload<KCalCore::Incidence::Ptr>(incidence);
+    item.setRevision(2);
+    mIncidencesToAdd = 1;
+    modifyJob = new ItemModifyJob(item,  this);
+    AKVERIFYEXEC(modifyJob);
+    waitForIt();
+    QCOMPARE(mCalendar->model()->rowCount(), anz+1);
+}
+
+
 void ETMCalendarTest::waitForIt()
 {
     QTestEventLoop::instance().enterLoop(10);
diff --git a/akonadi/calendar/tests/etmcalendartest.h b/akonadi/calendar/tests/etmcalendartest.h
index 1d219e7..22f3e38 100644
--- a/akonadi/calendar/tests/etmcalendartest.h
+++ b/akonadi/calendar/tests/etmcalendartest.h
@@ -50,6 +50,8 @@ private Q_SLOTS:
     void testUidChange();
     void testItem(); // tests item()
     void testShareETM();
+    void testFilterInvitations();
+    void testFilterInvitationsChanged();
 
 public Q_SLOTS:
     void calendarIncidenceAdded(const KCalCore::Incidence::Ptr &incidence);   /**Q_DECL_OVERRIDE*/


commit 50db79b548b767945ee9c5ae809cb73b0873e1eb
Author: Sandro Knauß <knauss at kolabsys.com>
Date:   Wed Sep 24 21:00:03 2014 +0200

    Used cached version of thatIsMe

diff --git a/kcalutils/incidenceformatter.cpp b/kcalutils/incidenceformatter.cpp
index e996f19..4ef8c1a 100644
--- a/kcalutils/incidenceformatter.cpp
+++ b/kcalutils/incidenceformatter.cpp
@@ -45,7 +45,7 @@
 #include <kcalcore/visitor.h>
 using namespace KCalCore;
 
-#include <kpimidentities/identitymanager.h>
+#include <kpimidentities/utils.h>
 
 #include <kpimutils/email.h>
 #include <kpimutils/linklocator.h>
@@ -78,44 +78,17 @@ static QString string2HTML(const QString &str)
     return KPIMUtils::LinkLocator::convertToHtml(str);
 }
 
-static KPIMIdentities::IdentityManager *s_identityManager = 0;
-
-// Performance optimization so we only create one IdentityManager instead of 1 per attendee.
-// Using RAII to protect against future return statements in the middle of code
-struct RAIIIdentityManager{
-    RAIIIdentityManager()
-    {
-        //t.start();
-        s_identityManager = new KPIMIdentities::IdentityManager(true);
-    }
-
-    ~RAIIIdentityManager()
-    {
-        delete s_identityManager;
-        s_identityManager = 0;
-        //qDebug() << "Elapsed time: " << t.elapsed();
-    }
-    //QElapsedTimer t;
-};
-
 static bool thatIsMe(const QString &email)
 {
-    return s_identityManager ? s_identityManager->thatIsMe(email)
-                             : KPIMIdentities::IdentityManager(true).thatIsMe(email);
+    return KPIMIdentities::thatIsMe(email);
 }
 
-static bool iamAttendee(Attendee::Ptr attendee)
+static bool iamAttendee(const Attendee::Ptr &attendee)
 {
     // Check if this attendee is the user
     return thatIsMe(attendee->email());
 }
 
-static bool iamPerson(const Person &person)
-{
-    // Check if this person is the user. test email only
-    return thatIsMe(person.email());
-}
-
 static QString htmlAddLink(const QString &ref, const QString &text,
                            bool newline = true)
 {
@@ -130,22 +103,20 @@ static QString htmlAddMailtoLink(const QString &email, const QString &name)
 {
     QString str;
 
-    if (!email.isEmpty()) {
+    if (!email.isEmpty() && !thatIsMe(email)) {
         Person person(name, email);
-        if (!iamPerson(person)) {     // do not add a link for the user's email
-            QString path = person.fullName().simplified();
-            if (path.isEmpty() || path.startsWith(QLatin1Char('"'))) {
-                path = email;
-            }
-            KUrl mailto;
-            mailto.setProtocol(QLatin1String("mailto"));
-            mailto.setPath(path);
-
-            // static for performance
-            static const QString iconPath =
-                KIconLoader::global()->iconPath(QLatin1String("mail-message-new"), KIconLoader::Small);
-            str = htmlAddLink(mailto.url(), QLatin1String("<img valign=\"top\" src=\"") + iconPath + QLatin1String("\">"));
+        QString path = person.fullName().simplified();
+        if (path.isEmpty() || path.startsWith(QLatin1Char('"'))) {
+            path = email;
         }
+        KUrl mailto;
+        mailto.setProtocol(QLatin1String("mailto"));
+        mailto.setPath(path);
+
+        // static for performance
+        static const QString iconPath =
+            KIconLoader::global()->iconPath(QLatin1String("mail-message-new"), KIconLoader::Small);
+        str = htmlAddLink(mailto.url(), QLatin1String("<img valign=\"top\" src=\"") + iconPath + QLatin1String("\">"));
     }
     return str;
 }
@@ -1318,7 +1289,6 @@ static Attendee::Ptr findDelegatedFromMyAttendee(const Incidence::Ptr &incidence
         return attendee;
     }
 
-    RAIIIdentityManager raiiHelper;
     QString delegatorName, delegatorEmail;
     Attendee::List attendees = incidence->attendees();
     Attendee::List::ConstIterator it;
@@ -1343,12 +1313,11 @@ static Attendee::Ptr findMyAttendee(const Incidence::Ptr &incidence)
         return attendee;
     }
 
-    RAIIIdentityManager raiiHelper;
     Attendee::List attendees = incidence->attendees();
     Attendee::List::ConstIterator it;
     for (it = attendees.constBegin(); it != attendees.constEnd(); ++it) {
         Attendee::Ptr a = *it;
-        if (thatIsMe(a->email())) {
+        if (iamAttendee(a)) {
             attendee = a;
             break;
         }
@@ -1367,7 +1336,6 @@ static Attendee::Ptr findAttendee(const Incidence::Ptr &incidence,
         return attendee;
     }
 
-    RAIIIdentityManager raiiHelper;
     Attendee::List attendees = incidence->attendees();
     Attendee::List::ConstIterator it;
     for (it = attendees.constBegin(); it != attendees.constEnd(); ++it) {
@@ -2408,8 +2376,6 @@ static QString invitationHeaderFreeBusy(const FreeBusy::Ptr &fb,
 
 static QString invitationAttendeeList(const Incidence::Ptr &incidence)
 {
-    RAIIIdentityManager raiiHelper;
-
     QString tmpStr;
     if (!incidence) {
         return tmpStr;


commit 759d61446af11fbb7fd6f260bf9cb0242fcabc1a
Author: Sandro Knauß <knauss at kolabsys.com>
Date:   Wed Sep 24 14:33:41 2014 +0200

    Used cached version of thatIsMe and allEmails

diff --git a/akonadi/calendar/utils_p.cpp b/akonadi/calendar/utils_p.cpp
index 21f7371..2bb60c7 100644
--- a/akonadi/calendar/utils_p.cpp
+++ b/akonadi/calendar/utils_p.cpp
@@ -22,6 +22,7 @@
 #include <kpimutils/email.h>
 #include <kpimidentities/identitymanager.h>
 #include <kpimidentities/identity.h>
+#include <kpimidentities/utils.h>
 #include <kmime/kmime_header_parsing.h>
 
 #include <KEMailSettings>
@@ -85,25 +86,13 @@ QString Akonadi::CalendarUtils::email()
     return emailSettings.getSetting(KEMailSettings::EmailAddress);
 }
 
-bool Akonadi::CalendarUtils::thatIsMe(const QString &_email)
+bool Akonadi::CalendarUtils::thatIsMe(const KCalCore::Attendee::Ptr &attendee)
 {
-    KPIMIdentities::IdentityManager identityManager(/*ro=*/ true);
-
-    // NOTE: this method is called for every created agenda view item,
-    // so we need to keep performance in mind
-
-    /* identityManager()->thatIsMe() is quite expensive since it does parsing of
-       _email in a way which is unnecessarily complex for what we can have here,
-       so we do that ourselves. This makes sense since this
-
-    if ( Akonadi::identityManager()->thatIsMe( _email ) ) {
-      return true;
-    }
-    */
+    return KPIMIdentities::thatIsMe(attendee->email());
+}
 
-    // in case email contains a full name, strip it out.
-    // the below is the simpler but slower version of the following code:
-    // const QString email = KPIM::getEmailAddress( _email );
+bool Akonadi::CalendarUtils::thatIsMe(const QString &_email)
+{
     const QByteArray tmp = _email.toUtf8();
     const char *cursor = tmp.constData();
     const char *end = tmp.data() + tmp.length();
@@ -111,30 +100,16 @@ bool Akonadi::CalendarUtils::thatIsMe(const QString &_email)
     KMime::HeaderParsing::parseMailbox(cursor, end, mbox);
     const QString email = mbox.addrSpec().asString();
 
-    KEMailSettings emailSettings;
-    const QString myEmail = emailSettings.getSetting(KEMailSettings::EmailAddress);
-
-    if (myEmail == email) {
-        return true;
-    }
-
-    KPIMIdentities::IdentityManager::ConstIterator it;
-    for (it = identityManager.begin();
-            it != identityManager.end(); ++it) {
-        if ((*it).matchesEmailAddress(email)) {
-            return true;
-        }
-    }
-
-    return false;
+    return KPIMIdentities::thatIsMe(email);
 }
 
 QStringList Akonadi::CalendarUtils::allEmails()
 {
-    KPIMIdentities::IdentityManager identityManager(/*ro=*/ true);
-    // Grab emails from the email identities
-    // Warning, this list could contain duplicates.
-    return identityManager.allEmails();
+    QStringList emails;
+    foreach(const QString &email, KPIMIdentities::allEmails()) {
+        emails.append(email);
+    }
+    return emails;
 }
 
 KCalCore::Incidence::Ptr Akonadi::CalendarUtils::incidence(const Akonadi::Item &item)
@@ -146,4 +121,4 @@ KCalCore::Incidence::Ptr Akonadi::CalendarUtils::incidence(const Akonadi::Item &
     } catch (Akonadi::PayloadException) {
         return KCalCore::Incidence::Ptr();
     }
-}
+}
\ No newline at end of file
diff --git a/akonadi/calendar/utils_p.h b/akonadi/calendar/utils_p.h
index f4cb12e..d024753 100644
--- a/akonadi/calendar/utils_p.h
+++ b/akonadi/calendar/utils_p.h
@@ -39,6 +39,10 @@ namespace CalendarUtils {
 QString fullName();
 QString email();
 bool thatIsMe(const QString &email);
+
+//faster version, because we know that attendee->email() is only the email address
+bool thatIsMe(const KCalCore::Attendee::Ptr &attendee);
+
 QStringList allEmails();
 
 KCalCore::Incidence::Ptr incidence(const Akonadi::Item &item);


commit 9b1869004ac7c9b7c6c0d6300efc6b4c0aa705f7
Author: Sandro Knauß <knauss at kolabsys.com>
Date:   Wed Sep 24 13:31:28 2014 +0200

    Added utils for cached version for thatIsMe and allEmails

diff --git a/kpimidentities/CMakeLists.txt b/kpimidentities/CMakeLists.txt
index 8957f69..2692c82 100644
--- a/kpimidentities/CMakeLists.txt
+++ b/kpimidentities/CMakeLists.txt
@@ -2,7 +2,7 @@
 add_definitions( -DQT_NO_CAST_FROM_ASCII )
 add_definitions( -DQT_NO_CAST_TO_ASCII )
 
-set(kpimidentities_LIB_SRCS identitymanager.cpp identity.cpp signature.cpp signatureconfigurator.cpp identitycombo.cpp)
+set(kpimidentities_LIB_SRCS identitymanager.cpp identity.cpp signature.cpp signatureconfigurator.cpp identitycombo.cpp utils.cpp)
 
 add_subdirectory( tests )
 
@@ -28,6 +28,7 @@ install(FILES
         signatureconfigurator.h
         identitymanager.h
         identitycombo.h
+        utils.h
         ${CMAKE_CURRENT_BINARY_DIR}/kpimidentities_export.h
         DESTINATION ${INCLUDE_INSTALL_DIR}/kpimidentities COMPONENT Devel)
 
diff --git a/kpimidentities/utils.cpp b/kpimidentities/utils.cpp
new file mode 100644
index 0000000..20f59a8
--- /dev/null
+++ b/kpimidentities/utils.cpp
@@ -0,0 +1,69 @@
+/*
+  Copyright (c) 2014 Sandro Knauß <knauss at kolabsys.com>
+
+  This library is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Library General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or ( at your
+  option ) any later version.
+
+  This library 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 Library General Public
+  License for more details.
+
+  You should have received a copy of the GNU Library General Public License
+  along with this library; see the file COPYING.LIB.  If not, write to the
+  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#include "utils.h"
+#include "identitymanager.h"
+
+#include <QObject>
+
+using namespace KPIMIdentities;
+
+class IdendentitiesCache : public QObject
+{
+    Q_OBJECT
+public:
+    IdendentitiesCache()
+        : mIdentityManager(/*ro=*/ true)
+    {
+        connect(&mIdentityManager, SIGNAL(changed()),
+                SLOT(slotIdentitiesChanged()));
+        slotIdentitiesChanged();
+    }
+    const QSet<QString> &emails()
+    {
+        return mEmails;
+    }
+
+private slots:
+    void slotIdentitiesChanged()
+    {
+        mEmails.clear();
+        foreach(const QString &email, mIdentityManager.allEmails()) {
+            mEmails.insert(email.toLower());
+        }
+    }
+
+private:
+    IdentityManager mIdentityManager;
+    QSet<QString> mEmails;
+};
+
+Q_GLOBAL_STATIC(IdendentitiesCache, sIdentitiesCache)
+
+bool KPIMIdentities::thatIsMe(const QString &email)
+{
+    return allEmails().contains(email.toLower());
+}
+
+const QSet<QString> & KPIMIdentities::allEmails()
+{
+    return sIdentitiesCache()->emails();
+}
+
+#include "utils.moc"
diff --git a/kpimidentities/utils.h b/kpimidentities/utils.h
new file mode 100644
index 0000000..98b5392
--- /dev/null
+++ b/kpimidentities/utils.h
@@ -0,0 +1,45 @@
+/*
+  Copyright (c) 2014 Sandro Knauß <knauss at kolabsys.com>
+
+  This library is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Library General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or ( at your
+  option ) any later version.
+
+  This library 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 Library General Public
+  License for more details.
+
+  You should have received a copy of the GNU Library General Public License
+  along with this library; see the file COPYING.LIB.  If not, write to the
+  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+*/
+
+#ifndef KPIMIDENTITES_UTILS_H
+#define KPIMIDENTITES_UTILS_H
+
+#include <kpimidentities/kpimidentities_export.h>
+
+#include <QString>
+#include <QSet>
+
+namespace KPIMIdentities
+{
+  /*
+   * Very fast version of IdentityManager::thatIsMe, that is using an internal cache (allEmails)
+   * - make sure that only an email address is used as parameter and NO name <email>
+   * - emails are tested with email.toLower(), so no need to lower them before.
+   */
+  KPIMIDENTITIES_EXPORT bool thatIsMe(const QString &email);
+
+  /*
+   * Very fast version of IdentityManager::allEmails , that is using an internal cache.
+   * The cache is updated with IdentityManager::changed signal.
+   * All email addresses + alias of the identities. The email addresses are all lowered.
+   */
+  KPIMIDENTITIES_EXPORT const QSet<QString> &allEmails();
+}
+
+#endif


commit 33eec57d4844da9682cce4be54e219c92f195547
Author: Sandro Knauß <knauss at kolabsys.com>
Date:   Wed Sep 3 13:28:46 2014 +0200

    Postpose button for invitations
    
    * show postpone button
    * handle postpone -> only store event in calendar
    * add test for postpone

diff --git a/akonadi/calendar/itiphandler.cpp b/akonadi/calendar/itiphandler.cpp
index ffaccf5..b3cb61c 100644
--- a/akonadi/calendar/itiphandler.cpp
+++ b/akonadi/calendar/itiphandler.cpp
@@ -221,6 +221,9 @@ void ITIPHandler::processiTIPMessage(const QString &receiver,
             d->m_scheduler->acceptCounterProposal(d->m_incidence, d->calendar());
         }
         return; // signal emitted in onSchedulerFinished().
+    } else if (action.startsWith(QLatin1String("request"))) {
+        d->m_scheduler->acceptTransaction(d->m_incidence, d->calendar(), d->m_method, status, receiver);
+        return;
     } else {
         kError() << "Unknown incoming action" << action;
 
diff --git a/akonadi/calendar/scheduler_p.cpp b/akonadi/calendar/scheduler_p.cpp
index 11e93c1..4c18a58 100644
--- a/akonadi/calendar/scheduler_p.cpp
+++ b/akonadi/calendar/scheduler_p.cpp
@@ -304,7 +304,7 @@ void Scheduler::acceptRequest(const IncidenceBase::Ptr &incidenceBase,
         KMessageBox::information(
             0,
             i18nc("@info",
-                  "<para>You accepted an invitation update, but an earlier version of the "
+                  "<para>You added an invitation update, but an earlier version of the "
                   "item could not be found in your calendar.</para>"
                   "<para>This may have occurred because:<list>"
                   "<item>the organizer did not include you in the original invitation</item>"
@@ -313,7 +313,7 @@ void Scheduler::acceptRequest(const IncidenceBase::Ptr &incidenceBase,
                   "<item>you no longer have access to the calendar containing the invitation</item>"
                   "</list></para>"
                   "<para>This is not a problem, but we thought you should know.</para>"),
-            i18nc("@title", "Cannot find invitation to be updated"), "AcceptCantFindIncidence");
+            i18nc("@title", "Cannot find invitation to be updated"), "CantFindIncidence");
     }
     kDebug() << "Storing new incidence with scheduling uid=" << schedulingUid << " and uid=" << incidence->uid();
 
diff --git a/akonadi/calendar/tests/itiphandlertest.cpp b/akonadi/calendar/tests/itiphandlertest.cpp
index 92c9e37..d7d85bb 100644
--- a/akonadi/calendar/tests/itiphandlertest.cpp
+++ b/akonadi/calendar/tests/itiphandlertest.cpp
@@ -182,6 +182,18 @@ void ITIPHandlerTest::testProcessITIPMessage_data()
                                  << expectedNumIncidences
                                  << expectedPartStat;
     //----------------------------------------------------------------------------------------------
+    // Process a REQUEST without having the incidence in our calendar.
+    // itiphandler should return success and add the rquest to a calendar
+    expectedResult = ITIPHandler::ResultSuccess;
+    data_filename = QLatin1String("invited_us");
+    expectedNumIncidences = 1;
+    expectedPartStat = KCalCore::Attendee::NeedsAction;
+    action = QLatin1String("request");
+    QTest::newRow("invited us5") << data_filename << action << receiver << incidenceUid
+                                 << expectedResult
+                                 << expectedNumIncidences
+                                 << expectedPartStat;
+    //----------------------------------------------------------------------------------------------
     // Here we're testing an error case, where data is null.
     expectedResult = ITIPHandler::ResultError;
     expectedNumIncidences = 0;
diff --git a/kcalutils/incidenceformatter.cpp b/kcalutils/incidenceformatter.cpp
index 215131c..e996f19 100644
--- a/kcalutils/incidenceformatter.cpp
+++ b/kcalutils/incidenceformatter.cpp
@@ -2928,6 +2928,10 @@ static QString responseButtons(const Incidence::Ptr &incidence,
         // Decline
         html += inviteButton(helper, QLatin1String("decline"),
                              i18nc("decline invitation", "Decline"));
+
+        // Postpone
+        html += inviteButton(helper, QLatin1String("postpone"),
+                             i18nc("postpone invitation", "Postpone"));
     }
 
     if (!rsvpRec || (incidence && incidence->revision() > 0)) {


commit c748944e39079c75e3b07d2676113869f887eb5e
Author: Sandro Knauß <knauss at kolabsys.com>
Date:   Tue Sep 2 14:56:43 2014 +0200

    ReadOnly is read from storageCollection

diff --git a/akonadi/calendar/calendarbase.cpp b/akonadi/calendar/calendarbase.cpp
index 4c49142..5cc7d8c 100644
--- a/akonadi/calendar/calendarbase.cpp
+++ b/akonadi/calendar/calendarbase.cpp
@@ -23,6 +23,7 @@
 #include "incidencechanger.h"
 #include "utils_p.h"
 
+#include <Akonadi/CollectionFetchJob>
 #include <akonadi/item.h>
 #include <akonadi/collection.h>
 
@@ -124,7 +125,20 @@ void CalendarBasePrivate::internalInsert(const Akonadi::Item &item)
     Akonadi::Collection collection = item.parentCollection();
     if (collection.isValid()) {
         // Some items don't have collection set
-        incidence->setReadOnly(!(collection.rights() & Akonadi::Collection::CanChangeItem));
+        if (item.storageCollectionId() !=  collection.id() && item.storageCollectionId() > -1) {
+            if (mCollections.contains(item.storageCollectionId())) {
+                collection = mCollections.value(item.storageCollectionId());
+                incidence->setReadOnly(!(collection.rights() & Akonadi::Collection::CanChangeItem));
+              } else if (!mCollectionJobs.key(item.storageCollectionId())) {
+                collection = Akonadi::Collection(item.storageCollectionId());
+                Akonadi::CollectionFetchJob *job = new Akonadi::CollectionFetchJob(collection, Akonadi::CollectionFetchJob::Base, this);
+                connect(job, SIGNAL(result(KJob*)), this, SLOT(collectionFetchResult(KJob*)));
+                mCollectionJobs.insert(job,  collection.id());
+            }
+        } else {
+            mCollections.insert(collection.id(), collection);
+            incidence->setReadOnly(!(collection.rights() & Akonadi::Collection::CanChangeItem));
+        }
     }
 
     mItemById.insert(item.id(), item);
@@ -149,6 +163,35 @@ void CalendarBasePrivate::internalInsert(const Akonadi::Item &item)
     }
 }
 
+void CalendarBasePrivate::collectionFetchResult(KJob* job)
+{
+    Akonadi::Collection::Id colid = mCollectionJobs.take(job);
+
+    if ( job->error() ) {
+        qDebug() << "Error occurred";
+        return;
+    }
+
+    Akonadi::CollectionFetchJob *fetchJob = qobject_cast<Akonadi::CollectionFetchJob*>( job );
+
+    const Akonadi::Collection collection = fetchJob->collections().first();
+    if (collection.id() !=  colid) {
+        kError() <<  "Fetched the wrong collection,  should fetch: " <<  colid << "fetched: " <<  collection.id();
+    }
+
+    bool isReadOnly = !(collection.rights() & Akonadi::Collection::CanChangeItem);
+    foreach (const Akonadi::Item &item, mItemsByCollection.values(collection.id())) {
+        KCalCore::Incidence::Ptr incidence = CalendarUtils::incidence(item);
+        incidence->setReadOnly(isReadOnly);
+    }
+
+    mCollections.insert(collection.id(),  collection);
+
+    if (mCollectionJobs.count() == 0) {
+        emit fetchFinished();
+    }
+}
+
 void CalendarBasePrivate::internalRemove(const Akonadi::Item &item)
 {
     Q_ASSERT(item.isValid());
diff --git a/akonadi/calendar/calendarbase_p.h b/akonadi/calendar/calendarbase_p.h
index 87bec31..eb233c3 100644
--- a/akonadi/calendar/calendarbase_p.h
+++ b/akonadi/calendar/calendarbase_p.h
@@ -64,8 +64,15 @@ public Q_SLOTS:
                             Akonadi::IncidenceChanger::ResultCode,
                             const QString &errorMessage);
 
+    void collectionFetchResult(KJob *job);
+
+signals:
+    void fetchFinished();
+
 public:
     QMultiHash<Akonadi::Collection::Id, Akonadi::Item> mItemsByCollection;
+    QHash<Akonadi::Collection::Id, Akonadi::Collection> mCollections;
+    QHash<KJob*, Akonadi::Collection::Id> mCollectionJobs;
     QHash<QString,Akonadi::Item::Id> mItemIdByUid;
     QHash<Akonadi::Item::Id, Akonadi::Item> mItemById;
     Akonadi::IncidenceChanger *mIncidenceChanger;
diff --git a/akonadi/calendar/fetchjobcalendar.cpp b/akonadi/calendar/fetchjobcalendar.cpp
index e1928de..a12d4d9 100644
--- a/akonadi/calendar/fetchjobcalendar.cpp
+++ b/akonadi/calendar/fetchjobcalendar.cpp
@@ -35,6 +35,8 @@ FetchJobCalendarPrivate::FetchJobCalendarPrivate(FetchJobCalendar *qq) : Calenda
     IncidenceFetchJob *job = new IncidenceFetchJob();
     connect(job, SIGNAL(result(KJob*)),
             SLOT(slotSearchJobFinished(KJob*)));
+    connect(this, SIGNAL(fetchFinished()),
+            SLOT(slotFetchJobFinished()));
 }
 
 FetchJobCalendarPrivate::~FetchJobCalendarPrivate()
@@ -44,27 +46,35 @@ FetchJobCalendarPrivate::~FetchJobCalendarPrivate()
 void FetchJobCalendarPrivate::slotSearchJobFinished(KJob *job)
 {
     IncidenceFetchJob *searchJob = static_cast<Akonadi::IncidenceFetchJob*>(job);
-    bool success = true;
-    QString errorMessage;
+    mSuccess = true;
+    mErrorMessage = QString();
     if (searchJob->error()) {
-        success = false;
-        errorMessage = searchJob->errorText();
+        mSuccess = false;
+        mErrorMessage = searchJob->errorText();
         kWarning() << "Unable to fetch incidences:" << searchJob->errorText();
     } else {
         foreach(const Akonadi::Item &item, searchJob->items()) {
             if (!item.isValid() || !item.hasPayload<KCalCore::Incidence::Ptr>()) {
-                success = false;
-                errorMessage = QString("Invalid item or payload: %1").arg(item.id());
-                kWarning() << "Unable to fetch incidences:" << errorMessage;
+                mSuccess = false;
+                mErrorMessage = QString("Invalid item or payload: %1").arg(item.id());
+                kWarning() << "Unable to fetch incidences:" << mErrorMessage;
                 continue;
             }
             internalInsert(item);
         }
     }
+
+    if (mCollectionJobs.count() ==  0) {
+        slotFetchJobFinished();
+    }
+}
+
+void FetchJobCalendarPrivate::slotFetchJobFinished()
+{
     m_isLoaded = true;
     // emit loadFinished() in a delayed manner, due to freezes because of execs.
     QMetaObject::invokeMethod(q, "loadFinished", Qt::QueuedConnection,
-                              Q_ARG(bool, success), Q_ARG(QString, errorMessage));
+                              Q_ARG(bool, mSuccess), Q_ARG(QString, mErrorMessage));
 }
 
 FetchJobCalendar::FetchJobCalendar(QObject *parent) : CalendarBase(new FetchJobCalendarPrivate(this), parent)
diff --git a/akonadi/calendar/fetchjobcalendar_p.h b/akonadi/calendar/fetchjobcalendar_p.h
index e3aba8f..5923cb1 100644
--- a/akonadi/calendar/fetchjobcalendar_p.h
+++ b/akonadi/calendar/fetchjobcalendar_p.h
@@ -38,12 +38,15 @@ public:
 
 public Q_SLOTS:
     void slotSearchJobFinished(KJob *job);
+    void slotFetchJobFinished();
 
 public:
     bool m_isLoaded;
 
 private:
     FetchJobCalendar *const q;
+    QString mErrorMessage;
+    bool mSuccess;
 };
 
 }





More information about the commits mailing list