summaryrefslogtreecommitdiff
path: root/kolabformatV2/incidence.cpp
diff options
context:
space:
mode:
authorChristian Mollekopf <mollekopf@kolabsys.com>2012-03-19 23:31:22 (GMT)
committerChristian Mollekopf <mollekopf@kolabsys.com>2012-03-19 23:31:22 (GMT)
commita6c64f52fea90968477e554b51883224c7b4144a (patch)
tree8ea63c6ba788bb3f9f7aed70918d4405ca1d3e25 /kolabformatV2/incidence.cpp
downloadlibkolab-a6c64f52fea90968477e554b51883224c7b4144a.tar.gz
Copied Kolab Format implementation from kdepim-runtime/resources/kolabproxy/
Copied from commit 4db1c180a37713293ee126deed1f97473ab64618 It's being moved out of kdepim-runtime so we can start using it for the upgrade-tool.
Diffstat (limited to 'kolabformatV2/incidence.cpp')
-rw-r--r--kolabformatV2/incidence.cpp976
1 files changed, 976 insertions, 0 deletions
diff --git a/kolabformatV2/incidence.cpp b/kolabformatV2/incidence.cpp
new file mode 100644
index 0000000..9f4323f
--- /dev/null
+++ b/kolabformatV2/incidence.cpp
@@ -0,0 +1,976 @@
+/*
+ This file is part of the kolab resource - the implementation of the
+ Kolab storage format. See www.kolab.org for documentation on this.
+
+ Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk>
+
+ 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.
+
+ In addition, as a special exception, the copyright holders give
+ permission to link the code of this program with any edition of
+ the Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), and distribute linked
+ combinations including the two. You must obey the GNU General
+ Public License in all respects for all of the code used other than
+ Qt. If you modify this file, you may extend this exception to
+ your version of the file, but you are not obligated to do so. If
+ you do not wish to do so, delete this exception statement from
+ your version.
+*/
+
+#include "incidence.h"
+#include "akonadi-version.h"
+
+#include <QList>
+
+#include <kcalcore/journal.h>
+#include <kdebug.h>
+#include <kurl.h>
+#include <kio/netaccess.h>
+
+#include <QBitArray>
+
+using namespace Kolab;
+
+
+Incidence::Incidence( const QString& tz, const KCalCore::Incidence::Ptr &incidence )
+ : KolabBase( tz ), mFloatingStatus( Unset ), mHasAlarm( false )
+{
+ Q_UNUSED( incidence );
+}
+
+Incidence::~Incidence()
+{
+}
+
+void Incidence::setSummary( const QString& summary )
+{
+ mSummary = summary;
+}
+
+QString Incidence::summary() const
+{
+ return mSummary;
+}
+
+void Incidence::setLocation( const QString& location )
+{
+ mLocation = location;
+}
+
+QString Incidence::location() const
+{
+ return mLocation;
+}
+
+void Incidence::setOrganizer( const Email& organizer )
+{
+ mOrganizer = organizer;
+}
+
+KolabBase::Email Incidence::organizer() const
+{
+ return mOrganizer;
+}
+
+void Incidence::setStartDate( const KDateTime& startDate )
+{
+ mStartDate = startDate;
+ if ( mFloatingStatus == AllDay )
+ kDebug() <<"ERROR: Time on start date but no time on the event";
+ mFloatingStatus = HasTime;
+}
+
+void Incidence::setStartDate( const QDate& startDate )
+{
+ mStartDate = KDateTime( startDate );
+ if ( mFloatingStatus == HasTime )
+ kDebug() <<"ERROR: No time on start date but time on the event";
+ mFloatingStatus = AllDay;
+}
+
+void Incidence::setStartDate( const QString& startDate )
+{
+ if ( startDate.length() > 10 )
+ // This is a date + time
+ setStartDate( stringToDateTime( startDate ) );
+ else
+ // This is only a date
+ setStartDate( stringToDate( startDate ) );
+}
+
+KDateTime Incidence::startDate() const
+{
+ return mStartDate;
+}
+
+void Incidence::setAlarm( float alarm )
+{
+ mAlarm = alarm;
+ mHasAlarm = true;
+}
+
+float Incidence::alarm() const
+{
+ return mAlarm;
+}
+
+Incidence::Recurrence Incidence::recurrence() const
+{
+ return mRecurrence;
+}
+
+void Incidence::addAttendee( const Attendee& attendee )
+{
+ mAttendees.append( attendee );
+}
+
+QList<Incidence::Attendee>& Incidence::attendees()
+{
+ return mAttendees;
+}
+
+const QList<Incidence::Attendee>& Incidence::attendees() const
+{
+ return mAttendees;
+}
+
+void Incidence::setInternalUID( const QString& iuid )
+{
+ mInternalUID = iuid;
+}
+
+QString Incidence::internalUID() const
+{
+ return mInternalUID;
+}
+
+bool Incidence::loadAttendeeAttribute( QDomElement& element,
+ Attendee& attendee )
+{
+ for ( QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+ if ( n.isComment() )
+ continue;
+ if ( n.isElement() ) {
+ QDomElement e = n.toElement();
+ QString tagName = e.tagName();
+
+ if ( tagName == "display-name" )
+ attendee.displayName = e.text();
+ else if ( tagName == "smtp-address" )
+ attendee.smtpAddress = e.text();
+ else if ( tagName == "status" )
+ attendee.status = e.text();
+ else if ( tagName == "request-response" )
+ // This sets reqResp to false, if the text is "false". Otherwise it
+ // sets it to true. This means the default setting is true.
+ attendee.requestResponse = ( e.text().toLower() != "false" );
+ else if ( tagName == "invitation-sent" )
+ // Like above, only this defaults to false
+ attendee.invitationSent = ( e.text().toLower() != "true" );
+ else if ( tagName == "role" )
+ attendee.role = e.text();
+ else if ( tagName == "delegated-to" )
+ attendee.delegate = e.text();
+ else if ( tagName == "delegated-from" )
+ attendee.delegator = e.text();
+ else
+ // TODO: Unhandled tag - save for later storage
+ kDebug() <<"Warning: Unhandled tag" << e.tagName();
+ } else
+ kDebug() <<"Node is not a comment or an element???";
+ }
+
+ return true;
+}
+
+void Incidence::saveAttendeeAttribute( QDomElement& element,
+ const Attendee& attendee ) const
+{
+ QDomElement e = element.ownerDocument().createElement( "attendee" );
+ element.appendChild( e );
+ writeString( e, "display-name", attendee.displayName );
+ writeString( e, "smtp-address", attendee.smtpAddress );
+ writeString( e, "status", attendee.status );
+ writeString( e, "request-response",
+ ( attendee.requestResponse ? "true" : "false" ) );
+ writeString( e, "invitation-sent",
+ ( attendee.invitationSent ? "true" : "false" ) );
+ writeString( e, "role", attendee.role );
+ writeString( e, "delegated-to", attendee.delegate );
+ writeString( e, "delegated-from", attendee.delegator );
+}
+
+void Incidence::saveAttendees( QDomElement& element ) const
+{
+ foreach ( const Attendee& attendee, mAttendees )
+ saveAttendeeAttribute( element, attendee );
+}
+
+void Incidence::saveAttachments( QDomElement& element ) const
+{
+ foreach ( KCalCore::Attachment::Ptr a, mAttachments ) {
+ if ( a->isUri() ) {
+ writeString( element, "link-attachment", a->uri() );
+ } else if ( a->isBinary() ) {
+ writeString( element, "inline-attachment", a->label() );
+ }
+ }
+}
+
+void Incidence::saveAlarms( QDomElement& element ) const
+{
+ if ( mAlarms.isEmpty() ) return;
+
+ QDomElement list = element.ownerDocument().createElement( "advanced-alarms" );
+ element.appendChild( list );
+ foreach ( KCalCore::Alarm::Ptr a, mAlarms ) {
+ QDomElement e = list.ownerDocument().createElement( "alarm" );
+ list.appendChild( e );
+
+ writeString( e, "enabled", a->enabled() ? "1" : "0" );
+ if ( a->hasStartOffset() ) {
+ writeString( e, "start-offset", QString::number( a->startOffset().asSeconds()/60 ) );
+ }
+ if ( a->hasEndOffset() ) {
+ writeString( e, "end-offset", QString::number( a->endOffset().asSeconds()/60 ) );
+ }
+ if ( a->repeatCount() ) {
+ writeString( e, "repeat-count", QString::number( a->repeatCount() ) );
+ writeString( e, "repeat-interval", QString::number( a->snoozeTime().asSeconds() ) );
+ }
+
+ switch ( a->type() ) {
+ case KCalCore::Alarm::Invalid:
+ break;
+ case KCalCore::Alarm::Display:
+ e.setAttribute( "type", "display" );
+ writeString( e, "text", a->text() );
+ break;
+ case KCalCore::Alarm::Procedure:
+ e.setAttribute( "type", "procedure" );
+ writeString( e, "program", a->programFile() );
+ writeString( e, "arguments", a->programArguments() );
+ break;
+ case KCalCore::Alarm::Email:
+ {
+ e.setAttribute( "type", "email" );
+ QDomElement addresses = e.ownerDocument().createElement( "addresses" );
+ e.appendChild( addresses );
+ foreach ( const KCalCore::Person::Ptr &person, a->mailAddresses() ) {
+ writeString( addresses, "address", person->fullName() );
+ }
+ writeString( e, "subject", a->mailSubject() );
+ writeString( e, "mail-text", a->mailText() );
+ QDomElement attachments = e.ownerDocument().createElement( "attachments" );
+ e.appendChild( attachments );
+ foreach ( const QString &attachment, a->mailAttachments() ) {
+ writeString( attachments, "attachment", attachment );
+ }
+ break;
+ }
+ case KCalCore::Alarm::Audio:
+ e.setAttribute( "type", "audio" );
+ writeString( e, "file", a->audioFile() );
+ break;
+ default:
+ kWarning() << "Unhandled alarm type:" << a->type();
+ break;
+ }
+ }
+}
+
+void Incidence::saveRecurrence( QDomElement& element ) const
+{
+ QDomElement e = element.ownerDocument().createElement( "recurrence" );
+ element.appendChild( e );
+ e.setAttribute( "cycle", mRecurrence.cycle );
+ if ( !mRecurrence.type.isEmpty() )
+ e.setAttribute( "type", mRecurrence.type );
+ writeString( e, "interval", QString::number( mRecurrence.interval ) );
+ foreach ( const QString& recurrence, mRecurrence.days ) {
+ writeString( e, "day", recurrence );
+ }
+ if ( !mRecurrence.dayNumber.isEmpty() )
+ writeString( e, "daynumber", mRecurrence.dayNumber );
+ if ( !mRecurrence.month.isEmpty() )
+ writeString( e, "month", mRecurrence.month );
+ if ( !mRecurrence.rangeType.isEmpty() ) {
+ QDomElement range = element.ownerDocument().createElement( "range" );
+ e.appendChild( range );
+ range.setAttribute( "type", mRecurrence.rangeType );
+ QDomText t = element.ownerDocument().createTextNode( mRecurrence.range );
+ range.appendChild( t );
+ }
+ foreach ( const QDate& date, mRecurrence.exclusions ) {
+ writeString( e, "exclusion", dateToString( date ) );
+ }
+}
+
+void Incidence::loadRecurrence( const QDomElement& element )
+{
+ mRecurrence.interval = 0;
+ mRecurrence.cycle = element.attribute( "cycle" );
+ mRecurrence.type = element.attribute( "type" );
+ for ( QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+ if ( n.isComment() )
+ continue;
+ if ( n.isElement() ) {
+ QDomElement e = n.toElement();
+ QString tagName = e.tagName();
+ if ( tagName == "interval" ) {
+ //kolab/issue4229, sometimes the interval value can be empty
+ if ( e.text().isEmpty() || e.text().toInt() <= 0 ) {
+ mRecurrence.interval = 1;
+ } else {
+ mRecurrence.interval = e.text().toInt();
+ }
+ }
+ else if ( tagName == "day" ) // can be present multiple times
+ mRecurrence.days.append( e.text() );
+ else if ( tagName == "daynumber" )
+ mRecurrence.dayNumber = e.text();
+ else if ( tagName == "month" )
+ mRecurrence.month = e.text();
+ else if ( tagName == "range" ) {
+ mRecurrence.rangeType = e.attribute( "type" );
+ mRecurrence.range = e.text();
+ } else if ( tagName == "exclusion" ) {
+ mRecurrence.exclusions.append( stringToDate( e.text() ) );
+ } else
+ // TODO: Unhandled tag - save for later storage
+ kDebug() <<"Warning: Unhandled tag" << e.tagName();
+ }
+ }
+}
+
+static void loadAddressesHelper( const QDomElement& element, const KCalCore::Alarm::Ptr &a )
+{
+ for ( QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+ if ( n.isComment() )
+ continue;
+ if ( n.isElement() ) {
+ QDomElement e = n.toElement();
+ QString tagName = e.tagName();
+
+ if ( tagName == "address" ) {
+ a->addMailAddress( KCalCore::Person::fromFullName( e.text() ) );
+ } else {
+ kWarning() << "Unhandled tag" << tagName;
+ }
+ }
+ }
+}
+
+static void loadAttachmentsHelper( const QDomElement& element, const KCalCore::Alarm::Ptr &a )
+{
+ for ( QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+ if ( n.isComment() )
+ continue;
+ if ( n.isElement() ) {
+ QDomElement e = n.toElement();
+ QString tagName = e.tagName();
+
+ if ( tagName == "attachment" ) {
+ a->addMailAttachment( e.text() );
+ } else {
+ kWarning() << "Unhandled tag" << tagName;
+ }
+ }
+ }
+}
+
+static void loadAlarmHelper( const QDomElement& element, const KCalCore::Alarm::Ptr &a )
+{
+ for ( QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+ if ( n.isComment() )
+ continue;
+ if ( n.isElement() ) {
+ QDomElement e = n.toElement();
+ QString tagName = e.tagName();
+
+ if ( tagName == "start-offset" ) {
+ a->setStartOffset( e.text().toInt()*60 );
+ } else if ( tagName == "end-offset" ) {
+ a->setEndOffset( e.text().toInt()*60 );
+ } else if ( tagName == "repeat-count" ) {
+ a->setRepeatCount( e.text().toInt() );
+ } else if ( tagName == "repeat-interval" ) {
+ a->setSnoozeTime( e.text().toInt() );
+ } else if ( tagName == "text" ) {
+ a->setText( e.text() );
+ } else if ( tagName == "program" ) {
+ a->setProgramFile( e.text() );
+ } else if ( tagName == "arguments" ) {
+ a->setProgramArguments( e.text() );
+ } else if ( tagName == "addresses" ) {
+ loadAddressesHelper( e, a );
+ } else if ( tagName == "subject" ) {
+ a->setMailSubject( e.text() );
+ } else if ( tagName == "mail-text" ) {
+ a->setMailText( e.text() );
+ } else if ( tagName == "attachments" ) {
+ loadAttachmentsHelper( e, a );
+ } else if ( tagName == "file" ) {
+ a->setAudioFile( e.text() );
+ } else if ( tagName == "enabled" ) {
+ a->setEnabled( e.text().toInt() != 0 );
+ } else {
+ kWarning() << "Unhandled tag" << tagName;
+ }
+ }
+ }
+}
+
+void Incidence::loadAlarms( const QDomElement& element )
+{
+ for ( QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+ if ( n.isComment() )
+ continue;
+ if ( n.isElement() ) {
+ QDomElement e = n.toElement();
+ QString tagName = e.tagName();
+
+ if ( tagName == "alarm" ) {
+ KCalCore::Alarm::Ptr a = KCalCore::Alarm::Ptr( new KCalCore::Alarm( 0 ) );
+ a->setEnabled( true ); // default to enabled, unless some XML attribute says otherwise.
+ QString type = e.attribute( "type" );
+ if ( type == "display" ) {
+ a->setType( KCalCore::Alarm::Display );
+ } else if ( type == "procedure" ) {
+ a->setType( KCalCore::Alarm::Procedure );
+ } else if ( type == "email" ) {
+ a->setType( KCalCore::Alarm::Email );
+ } else if ( type == "audio" ) {
+ a->setType( KCalCore::Alarm::Audio );
+ } else {
+ kWarning() << "Unhandled alarm type:" << type;
+ }
+
+ loadAlarmHelper( e, a );
+ mAlarms << a;
+ } else {
+ kWarning() << "Unhandled tag" << tagName;
+ }
+ }
+ }
+}
+
+bool Incidence::loadAttribute( QDomElement& element )
+{
+ QString tagName = element.tagName();
+
+ if ( tagName == "summary" )
+ setSummary( element.text() );
+ else if ( tagName == "location" )
+ setLocation( element.text() );
+ else if ( tagName == "organizer" ) {
+ Email email;
+ if ( loadEmailAttribute( element, email ) ) {
+ setOrganizer( email );
+ return true;
+ } else
+ return false;
+ } else if ( tagName == "start-date" )
+ setStartDate( element.text() );
+ else if ( tagName == "recurrence" )
+ loadRecurrence( element );
+ else if ( tagName == "attendee" ) {
+ Attendee attendee;
+ if ( loadAttendeeAttribute( element, attendee ) ) {
+ addAttendee( attendee );
+ return true;
+ } else
+ return false;
+ } else if ( tagName == "link-attachment" ) {
+ mAttachments.push_back( KCalCore::Attachment::Ptr( new KCalCore::Attachment( element.text() ) ) );
+ } else if ( tagName == "alarm" )
+ // Alarms should be minutes before. Libkcal uses event time + alarm time
+ setAlarm( - element.text().toInt() );
+ else if ( tagName == "advanced-alarms" )
+ loadAlarms( element );
+ else if ( tagName == "x-kde-internaluid" )
+ setInternalUID( element.text() );
+ else if ( tagName == "x-custom" ) {
+ loadCustomAttributes( element );
+ } else if ( tagName == "inline-attachment" ) {
+ // we handle that separately later on, so no need to create a KolabUnhandled entry for it
+ } else {
+ bool ok = KolabBase::loadAttribute( element );
+ if ( !ok ) {
+ // Unhandled tag - save for later storage
+ kDebug() <<"Saving unhandled tag" << element.tagName();
+ Custom c;
+ c.key = QByteArray( "X-KDE-KolabUnhandled-" ) + element.tagName().toLatin1();
+ c.value = element.text();
+ mCustomList.append( c );
+ }
+ }
+ // We handled this
+ return true;
+}
+
+bool Incidence::saveAttributes( QDomElement& element ) const
+{
+ // Save the base class elements
+ KolabBase::saveAttributes( element );
+
+ if ( mFloatingStatus == HasTime )
+ writeString( element, "start-date", dateTimeToString( startDate() ) );
+ else
+ writeString( element, "start-date", dateToString( startDate().date() ) );
+ writeString( element, "summary", summary() );
+ writeString( element, "location", location() );
+ saveEmailAttribute( element, organizer(), "organizer" );
+ if ( !mRecurrence.cycle.isEmpty() )
+ saveRecurrence( element );
+ saveAttendees( element );
+ saveAttachments( element );
+ if ( mHasAlarm ) {
+ // Alarms should be minutes before. Libkcal uses event time + alarm time
+ int alarmTime = qRound( -alarm() );
+ writeString( element, "alarm", QString::number( alarmTime ) );
+ }
+ saveAlarms( element );
+ writeString( element, "x-kde-internaluid", internalUID() );
+ saveCustomAttributes( element );
+ return true;
+}
+
+void Incidence::saveCustomAttributes( QDomElement& element ) const
+{
+ foreach ( const Custom& custom, mCustomList ) {
+ QString key( custom.key );
+ Q_ASSERT( !key.isEmpty() );
+ if ( key.startsWith( QLatin1String( "X-KDE-KolabUnhandled-" ) ) ) {
+ key = key.mid( strlen( "X-KDE-KolabUnhandled-" ) );
+ writeString( element, key, custom.value );
+ } else {
+ // Let's use attributes so that other tag-preserving-code doesn't need sub-elements
+ QDomElement e = element.ownerDocument().createElement( "x-custom" );
+ element.appendChild( e );
+ e.setAttribute( "key", key );
+ e.setAttribute( "value", custom.value );
+ }
+ }
+}
+
+void Incidence::loadCustomAttributes( QDomElement& element )
+{
+ Custom custom;
+ custom.key = element.attribute( "key" ).toLatin1();
+ custom.value = element.attribute( "value" );
+ mCustomList.append( custom );
+}
+
+static KCalCore::Attendee::PartStat attendeeStringToStatus( const QString& s )
+{
+ if ( s == "none" )
+ return KCalCore::Attendee::NeedsAction;
+ if ( s == "tentative" )
+ return KCalCore::Attendee::Tentative;
+ if ( s == "declined" )
+ return KCalCore::Attendee::Declined;
+ if ( s == "delegated" )
+ return KCalCore::Attendee::Delegated;
+
+ // Default:
+ return KCalCore::Attendee::Accepted;
+}
+
+static QString attendeeStatusToString( KCalCore::Attendee::PartStat status )
+{
+ switch( status ) {
+ case KCalCore::Attendee::NeedsAction:
+ return "none";
+ case KCalCore::Attendee::Accepted:
+ return "accepted";
+ case KCalCore::Attendee::Declined:
+ return "declined";
+ case KCalCore::Attendee::Tentative:
+ return "tentative";
+ case KCalCore::Attendee::Delegated:
+ return "delegated";
+ case KCalCore::Attendee::Completed:
+ case KCalCore::Attendee::InProcess:
+ // These don't have any meaning in the Kolab format, so just use:
+ return "accepted";
+ default:
+ // Default for the case that there are more added later:
+ return "accepted";
+ }
+}
+
+static KCalCore::Attendee::Role attendeeStringToRole( const QString& s )
+{
+ if ( s == "optional" )
+ return KCalCore::Attendee::OptParticipant;
+ if ( s == "resource" )
+ return KCalCore::Attendee::NonParticipant;
+ return KCalCore::Attendee::ReqParticipant;
+}
+
+static QString attendeeRoleToString( KCalCore::Attendee::Role role )
+{
+ switch( role ) {
+ case KCalCore::Attendee::ReqParticipant:
+ return "required";
+ case KCalCore::Attendee::OptParticipant:
+ return "optional";
+ case KCalCore::Attendee::Chair:
+ // We don't have the notion of chair, so use
+ return "required";
+ case KCalCore::Attendee::NonParticipant:
+ // In Kolab, a non-participant is a resource
+ return "resource";
+ }
+
+ // Default for the case that there are more added later:
+ return "required";
+}
+
+static const char *s_weekDayName[] =
+{
+ "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"
+};
+
+static const char *s_monthName[] =
+{
+ "january", "february", "march", "april", "may", "june", "july",
+ "august", "september", "october", "november", "december"
+};
+
+void Incidence::setRecurrence( KCalCore::Recurrence* recur )
+{
+ mRecurrence.interval = recur->frequency();
+ switch ( recur->recurrenceType() ) {
+ case KCalCore::Recurrence::rMinutely: // Not handled by the kolab XML
+ mRecurrence.cycle = "minutely";
+ break;
+ case KCalCore::Recurrence::rHourly: // Not handled by the kolab XML
+ mRecurrence.cycle = "hourly";
+ break;
+ case KCalCore::Recurrence::rDaily:
+ mRecurrence.cycle = "daily";
+ break;
+ case KCalCore::Recurrence::rWeekly: // every X weeks
+ mRecurrence.cycle = "weekly";
+ {
+ QBitArray arr = recur->days();
+ for ( uint idx = 0 ; idx < 7 ; ++idx )
+ if ( arr.testBit( idx ) )
+ mRecurrence.days.append( s_weekDayName[idx] );
+ }
+ break;
+ case KCalCore::Recurrence::rMonthlyPos: {
+ mRecurrence.cycle = "monthly";
+ mRecurrence.type = "weekday";
+ QList<KCalCore::RecurrenceRule::WDayPos> monthPositions = recur->monthPositions();
+ if ( !monthPositions.isEmpty() ) {
+ KCalCore::RecurrenceRule::WDayPos monthPos = monthPositions.first();
+ // TODO: Handle multiple days in the same week
+ mRecurrence.dayNumber = QString::number( monthPos.pos() );
+ mRecurrence.days.append( s_weekDayName[ monthPos.day()-1 ] );
+ // Not (properly) handled(?): monthPos.negative (nth days before end of month)
+ }
+ break;
+ }
+ case KCalCore::Recurrence::rMonthlyDay: {
+ mRecurrence.cycle = "monthly";
+ mRecurrence.type = "daynumber";
+ QList<int> monthDays = recur->monthDays();
+ // ####### Kolab XML limitation: only the first month day is used
+ if ( !monthDays.isEmpty() )
+ mRecurrence.dayNumber = QString::number( monthDays.first() );
+ break;
+ }
+ case KCalCore::Recurrence::rYearlyMonth: // (day n of Month Y)
+ {
+ mRecurrence.cycle = "yearly";
+ mRecurrence.type = "monthday";
+ QList<int> rmd = recur->yearDates();
+ int day = !rmd.isEmpty() ? rmd.first() : recur->startDate().day();
+ mRecurrence.dayNumber = QString::number( day );
+ QList<int> months = recur->yearMonths();
+ if ( !months.isEmpty() )
+ mRecurrence.month = s_monthName[ months.first() - 1 ]; // #### Kolab XML limitation: only one month specified
+ break;
+ }
+ case KCalCore::Recurrence::rYearlyDay: // YearlyDay (day N of the year). Not supported by Outlook
+ mRecurrence.cycle = "yearly";
+ mRecurrence.type = "yearday";
+ mRecurrence.dayNumber = QString::number( recur->yearDays().first() );
+ break;
+ case KCalCore::Recurrence::rYearlyPos: // (weekday X of week N of month Y)
+ mRecurrence.cycle = "yearly";
+ mRecurrence.type = "weekday";
+ QList<int> months = recur->yearMonths();
+ if ( !months.isEmpty() )
+ mRecurrence.month = s_monthName[ months.first() - 1 ]; // #### Kolab XML limitation: only one month specified
+ QList<KCalCore::RecurrenceRule::WDayPos> monthPositions = recur->yearPositions();
+ if ( !monthPositions.isEmpty() ) {
+ KCalCore::RecurrenceRule::WDayPos monthPos = monthPositions.first();
+ // TODO: Handle multiple days in the same week
+ mRecurrence.dayNumber = QString::number( monthPos.pos() );
+ mRecurrence.days.append( s_weekDayName[ monthPos.day()-1 ] );
+
+ //mRecurrence.dayNumber = QString::number( *recur->yearNums().getFirst() );
+ // Not handled: monthPos.negative (nth days before end of month)
+ }
+ break;
+ }
+ int howMany = recur->duration();
+ if ( howMany > 0 ) {
+ mRecurrence.rangeType = "number";
+ mRecurrence.range = QString::number( howMany );
+ } else if ( howMany == 0 ) {
+ mRecurrence.rangeType = "date";
+ mRecurrence.range = dateToString( recur->endDate() );
+ } else {
+ mRecurrence.rangeType = "none";
+ }
+}
+
+void Incidence::setFields( const KCalCore::Incidence::Ptr &incidence )
+{
+ KolabBase::setFields( incidence );
+
+ if ( incidence->allDay() ) {
+ // This is a all-day event. Don't timezone move this one
+ mFloatingStatus = AllDay;
+ setStartDate( incidence->dtStart().date() );
+ } else {
+ mFloatingStatus = HasTime;
+ setStartDate( localToUTC( incidence->dtStart() ) );
+ }
+
+ setSummary( incidence->summary() );
+ setLocation( incidence->location() );
+
+ // Alarm
+ mHasAlarm = false; // Will be set to true, if we actually have one
+ if ( incidence->hasEnabledAlarms() ) {
+ const KCalCore::Alarm::List& alarms = incidence->alarms();
+ if ( !alarms.isEmpty() ) {
+ const KCalCore::Alarm::Ptr alarm = alarms.first();
+ if ( alarm->hasStartOffset() ) {
+ int dur = alarm->startOffset().asSeconds();
+ setAlarm( (float)dur / 60.0 );
+ }
+ }
+ }
+
+ Email org( incidence->organizer()->name(), incidence->organizer()->email() );
+ setOrganizer( org );
+
+ // Attendees:
+ KCalCore::Attendee::List attendees = incidence->attendees();
+ foreach ( KCalCore::Attendee::Ptr kcalAttendee, attendees ) {
+ Attendee attendee;
+
+ attendee.displayName = kcalAttendee->name();
+ attendee.smtpAddress = kcalAttendee->email();
+ attendee.status = attendeeStatusToString( kcalAttendee->status() );
+ attendee.requestResponse = kcalAttendee->RSVP();
+ // TODO: KCalCore::Attendee::mFlag is not accessible
+ // attendee.invitationSent = kcalAttendee->mFlag;
+ // DF: Hmm? mFlag is set to true and never used at all.... Did you mean another field?
+ attendee.role = attendeeRoleToString( kcalAttendee->role() );
+ attendee.delegate = kcalAttendee->delegate();
+ attendee.delegator = kcalAttendee->delegator();
+
+ addAttendee( attendee );
+ }
+
+ mAttachments.clear();
+
+ // Attachments
+ KCalCore::Attachment::List attachments = incidence->attachments();
+ foreach ( KCalCore::Attachment::Ptr a, attachments ) {
+ mAttachments.push_back( a );
+ }
+
+ mAlarms.clear();
+
+ // Alarms
+ KCalCore::Alarm::List alarms = incidence->alarms();
+ foreach ( KCalCore::Alarm::Ptr a, alarms ) {
+ mAlarms.push_back( a );
+ }
+
+ if ( incidence->recurs() ) {
+ setRecurrence( incidence->recurrence() );
+ mRecurrence.exclusions = incidence->recurrence()->exDates();
+ }
+
+ // Handle the scheduling ID
+ if ( incidence->schedulingID() == incidence->uid() ) {
+ // There is no scheduling ID
+ setInternalUID( QString::null ); //krazy:exclude=nullstrassign for old broken gcc
+ } else {
+ // We've internally been using a different uid, so save that as the
+ // temporary (internal) uid and restore the original uid, the one that
+ // is used in the folder and the outside world
+ setUid( incidence->schedulingID() );
+ setInternalUID( incidence->uid() );
+ }
+
+ // Unhandled tags and other custom properties (see libkcal/customproperties.h)
+ const QMap<QByteArray, QString> map = incidence->customProperties();
+ QMap<QByteArray, QString>::ConstIterator cit = map.begin();
+ for ( ; cit != map.end() ; ++cit ) {
+ Custom c;
+ c.key = cit.key();
+ c.value = cit.value();
+ mCustomList.append( c );
+ }
+}
+
+static QBitArray daysListToBitArray( const QStringList& days )
+{
+ QBitArray arr( 7 );
+ arr.fill( false );
+ foreach ( const QString& day, days ) {
+ for ( uint i = 0; i < 7 ; ++i )
+ if ( day == s_weekDayName[i] )
+ arr.setBit( i, true );
+ }
+ return arr;
+}
+
+
+void Incidence::saveTo( const KCalCore::Incidence::Ptr &incidence )
+{
+ KolabBase::saveTo( incidence );
+
+ if ( mFloatingStatus == AllDay ) {
+ // This is an all-day event. Don't timezone move this one
+ incidence->setDtStart( startDate() );
+ incidence->setAllDay( true );
+ } else {
+ incidence->setDtStart( utcToLocal( startDate() ) );
+ incidence->setAllDay( false );
+ }
+
+ incidence->setSummary( summary() );
+ incidence->setLocation( location() );
+
+ if ( mHasAlarm && mAlarms.isEmpty() ) {
+ KCalCore::Alarm::Ptr alarm = incidence->newAlarm();
+ alarm->setStartOffset( qRound( mAlarm * 60.0 ) );
+ alarm->setEnabled( true );
+ alarm->setType( KCalCore::Alarm::Display );
+ } else if ( !mAlarms.isEmpty() ) {
+ foreach ( KCalCore::Alarm::Ptr a, mAlarms ) {
+ a->setParent( incidence.data() );
+ incidence->addAlarm( a );
+ }
+ }
+
+ if ( organizer().displayName.isEmpty() )
+ incidence->setOrganizer( organizer().smtpAddress );
+ else
+ incidence->setOrganizer( organizer().displayName + '<'
+ + organizer().smtpAddress + '>' );
+
+ incidence->clearAttendees();
+ foreach ( const Attendee& attendee, mAttendees ) {
+ KCalCore::Attendee::PartStat status = attendeeStringToStatus( attendee.status );
+ KCalCore::Attendee::Role role = attendeeStringToRole( attendee.role );
+ KCalCore::Attendee::Ptr a( new KCalCore::Attendee( attendee.displayName,
+ attendee.smtpAddress,
+ attendee.requestResponse,
+ status, role ) );
+ a->setDelegate( attendee.delegate );
+ a->setDelegator( attendee.delegator );
+ incidence->addAttendee( a );
+ }
+
+ incidence->clearAttachments();
+ foreach ( KCalCore::Attachment::Ptr a, mAttachments ) {
+ // TODO should we copy?
+ incidence->addAttachment( a );
+ }
+
+ if ( !mRecurrence.cycle.isEmpty() ) {
+ KCalCore::Recurrence* recur = incidence->recurrence(); // yeah, this creates it
+ // done below recur->setFrequency( mRecurrence.interval );
+ if ( mRecurrence.cycle == "minutely" ) {
+ recur->setMinutely( mRecurrence.interval );
+ } else if ( mRecurrence.cycle == "hourly" ) {
+ recur->setHourly( mRecurrence.interval );
+ } else if ( mRecurrence.cycle == "daily" ) {
+ recur->setDaily( mRecurrence.interval );
+ } else if ( mRecurrence.cycle == "weekly" ) {
+ QBitArray rDays = daysListToBitArray( mRecurrence.days );
+ recur->setWeekly( mRecurrence.interval, rDays );
+ } else if ( mRecurrence.cycle == "monthly" ) {
+ recur->setMonthly( mRecurrence.interval );
+ if ( mRecurrence.type == "weekday" ) {
+ recur->addMonthlyPos( mRecurrence.dayNumber.toInt(), daysListToBitArray( mRecurrence.days ) );
+ } else if ( mRecurrence.type == "daynumber" ) {
+ recur->addMonthlyDate( mRecurrence.dayNumber.toInt() );
+ } else kWarning() <<"Unhandled monthly recurrence type" << mRecurrence.type;
+ } else if ( mRecurrence.cycle == "yearly" ) {
+ recur->setYearly( mRecurrence.interval );
+ if ( mRecurrence.type == "monthday" ) {
+ recur->addYearlyDate( mRecurrence.dayNumber.toInt() );
+ for ( int i = 0; i < 12; ++i )
+ if ( s_monthName[ i ] == mRecurrence.month )
+ recur->addYearlyMonth( i+1 );
+ } else if ( mRecurrence.type == "yearday" ) {
+ recur->addYearlyDay( mRecurrence.dayNumber.toInt() );
+ } else if ( mRecurrence.type == "weekday" ) {
+ for ( int i = 0; i < 12; ++i )
+ if ( s_monthName[ i ] == mRecurrence.month )
+ recur->addYearlyMonth( i+1 );
+ recur->addYearlyPos( mRecurrence.dayNumber.toInt(), daysListToBitArray( mRecurrence.days ) );
+ } else kWarning() <<"Unhandled yearly recurrence type" << mRecurrence.type;
+ } else kWarning() <<"Unhandled recurrence cycle" << mRecurrence.cycle;
+
+ if ( mRecurrence.rangeType == "number" ) {
+ recur->setDuration( mRecurrence.range.toInt() );
+ } else if ( mRecurrence.rangeType == "date" ) {
+ recur->setEndDate( stringToDate( mRecurrence.range ) );
+ } // "none" is default since tje set*ly methods set infinite recurrence
+
+ incidence->recurrence()->setExDates( mRecurrence.exclusions );
+
+ }
+ /* If we've stored a uid to be used internally instead of the real one
+ * (to deal with duplicates of events in different folders) before, then
+ * restore it, so it does not change. Keep the original uid around for
+ * scheduling purposes. */
+ if ( !internalUID().isEmpty() ) {
+ incidence->setUid( internalUID() );
+ incidence->setSchedulingID( uid() );
+ }
+
+ foreach ( const Custom& custom, mCustomList ) {
+ incidence->setNonKDECustomProperty( custom.key, custom.value );
+ }
+
+}
+
+QString Incidence::productID() const
+{
+ return QString( "Akonadi %1, Kolab resource" ).arg( AKONADI_VERSION );
+}
+
+// Unhandled KCalCore::Incidence fields:
+// revision, status (unused), priority (done in tasks), attendee.uid,
+// mComments, mReadOnly
+