diff options
Diffstat (limited to 'tderesources/kolab/kcal/incidence.cpp')
-rw-r--r-- | tderesources/kolab/kcal/incidence.cpp | 1039 |
1 files changed, 1039 insertions, 0 deletions
diff --git a/tderesources/kolab/kcal/incidence.cpp b/tderesources/kolab/kcal/incidence.cpp new file mode 100644 index 000000000..ddc31491b --- /dev/null +++ b/tderesources/kolab/kcal/incidence.cpp @@ -0,0 +1,1039 @@ +/* + 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 TQt library by Trolltech AS, Norway (or with modified versions + of TQt that use the same license as TQt), 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 + TQt. 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 "resourcekolab.h" + +#include <tqfile.h> +#include <tqvaluelist.h> + +#include <libkcal/journal.h> +#include <korganizer/version.h> +#include <libemailfunctions/email.h> + +#include <kdebug.h> +#include <kmdcodec.h> +#include <kurl.h> +#include <kio/netaccess.h> + +using namespace Kolab; + + +Incidence::Incidence( KCal::ResourceKolab *res, const TQString &subResource, TQ_UINT32 sernum, + const TQString& tz ) + : KolabBase( tz ), mFloatingStatus( Unset ), mHasAlarm( false ), + mResource( res ), + mSubResource( subResource ), + mSernum( sernum ) +{ +} + +Incidence::~Incidence() +{ +} + +void Incidence::setSummary( const TQString& summary ) +{ + mSummary = summary; +} + +TQString Incidence::summary() const +{ + return mSummary; +} + +void Incidence::setLocation( const TQString& location ) +{ + mLocation = location; +} + +TQString Incidence::location() const +{ + return mLocation; +} + +void Incidence::setOrganizer( const Email& organizer ) +{ + mOrganizer = organizer; +} + +KolabBase::Email Incidence::organizer() const +{ + return mOrganizer; +} + +void Incidence::setStartDate( const TQDateTime& startDate ) +{ + mStartDate = startDate; + if ( mFloatingStatus == AllDay ) + kdDebug() << "ERROR: Time on start date but no time on the event\n"; + mFloatingStatus = HasTime; +} + +void Incidence::setStartDate( const TQDate& startDate ) +{ + mStartDate = startDate; + if ( mFloatingStatus == HasTime ) + kdDebug() << "ERROR: No time on start date but time on the event\n"; + mFloatingStatus = AllDay; +} + +void Incidence::setStartDate( const TQString& startDate ) +{ + if ( startDate.length() > 10 ) + // This is a date + time + setStartDate( stringToDateTime( startDate ) ); + else + // This is only a date + setStartDate( stringToDate( startDate ) ); +} + +TQDateTime 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 ); +} + +TQValueList<Incidence::Attendee>& Incidence::attendees() +{ + return mAttendees; +} + +const TQValueList<Incidence::Attendee>& Incidence::attendees() const +{ + return mAttendees; +} + +void Incidence::setInternalUID( const TQString& iuid ) +{ + mInternalUID = iuid; +} + +TQString Incidence::internalUID() const +{ + return mInternalUID; +} + +bool Incidence::loadAttendeeAttribute( TQDomElement& element, + Attendee& attendee ) +{ + for ( TQDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) { + if ( n.isComment() ) + continue; + if ( n.isElement() ) { + TQDomElement e = n.toElement(); + TQString tagName = e.tagName(); + + if ( tagName == "display-name" ) { + // Quote the text in case it contains commas or other quotable chars. + TQString tusername = KPIM::quoteNameIfNecessary( e.text() ); + + TQString tname, temail; + // ignore the return value because it will always be false since + // tusername does not contain "@domain". + KPIM::getNameAndMail( tusername, tname, temail ); + attendee.displayName = tname; + } + 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().lower() != "false" ); + else if ( tagName == "invitation-sent" ) + // Like above, only this defaults to false + attendee.invitationSent = ( e.text().lower() != "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 + kdDebug() << "Warning: Unhandled tag " << e.tagName() << endl; + } else + kdDebug() << "Node is not a comment or an element???" << endl; + } + + return true; +} + +void Incidence::saveAttendeeAttribute( TQDomElement& element, + const Attendee& attendee ) const +{ + TQDomElement 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( TQDomElement& element ) const +{ + TQValueList<Attendee>::ConstIterator it = mAttendees.begin(); + for ( ; it != mAttendees.end(); ++it ) + saveAttendeeAttribute( element, *it ); +} + +void Incidence::saveAttachments( TQDomElement& element ) const +{ + KCal::Attachment::List::ConstIterator it = mAttachments.begin(); + for ( ; it != mAttachments.end(); ++it ) { + KCal::Attachment *a = (*it); + if ( a->isUri() ) { + writeString( element, "link-attachment", a->uri() ); + } else if ( a->isBinary() ) { + writeString( element, "inline-attachment", a->label() ); + } + } +} + +void Incidence::saveAlarms( TQDomElement& element ) const +{ + if ( mAlarms.isEmpty() ) return; + + TQDomElement list = element.ownerDocument().createElement( "advanced-alarms" ); + element.appendChild( list ); + for ( KCal::Alarm::List::ConstIterator it = mAlarms.constBegin(); it != mAlarms.constEnd(); ++it ) { + KCal::Alarm* a = *it; + TQDomElement e = list.ownerDocument().createElement( "alarm" ); + list.appendChild( e ); + + writeString( e, "enabled", a->enabled() ? "1" : "0" ); + if ( a->hasStartOffset() ) { + writeString( e, "start-offset", TQString::number( a->startOffset().asSeconds()/60 ) ); + } + if ( a->hasEndOffset() ) { + writeString( e, "end-offset", TQString::number( a->endOffset().asSeconds()/60 ) ); + } + if ( a->repeatCount() ) { + writeString( e, "repeat-count", TQString::number( a->repeatCount() ) ); + writeString( e, "repeat-interval", TQString::number( a->snoozeTime() ) ); + } + + switch ( a->type() ) { + case KCal::Alarm::Invalid: + break; + case KCal::Alarm::Display: + e.setAttribute( "type", "display" ); + writeString( e, "text", a->text() ); + break; + case KCal::Alarm::Procedure: + e.setAttribute( "type", "procedure" ); + writeString( e, "program", a->programFile() ); + writeString( e, "arguments", a->programArguments() ); + break; + case KCal::Alarm::Email: + { + e.setAttribute( "type", "email" ); + TQDomElement addresses = e.ownerDocument().createElement( "addresses" ); + e.appendChild( addresses ); + for ( TQValueList<KCal::Person>::ConstIterator it = a->mailAddresses().constBegin(); it != a->mailAddresses().constEnd(); ++it ) { + writeString( addresses, "address", (*it).fullName() ); + } + writeString( e, "subject", a->mailSubject() ); + writeString( e, "mail-text", a->mailText() ); + TQDomElement attachments = e.ownerDocument().createElement( "attachments" ); + e.appendChild( attachments ); + for ( TQStringList::ConstIterator it = a->mailAttachments().constBegin(); it != a->mailAttachments().constEnd(); ++it ) { + writeString( attachments, "attachment", *it ); + } + break; + } + case KCal::Alarm::Audio: + e.setAttribute( "type", "audio" ); + writeString( e, "file", a->audioFile() ); + break; + default: + kdWarning() << "Unhandled alarm type:" << a->type() << endl; + break; + } + } +} + +void Incidence::saveRecurrence( TQDomElement& element ) const +{ + TQDomElement 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", TQString::number( mRecurrence.interval ) ); + for( TQStringList::ConstIterator it = mRecurrence.days.begin(); it != mRecurrence.days.end(); ++it ) { + writeString( e, "day", *it ); + } + if ( !mRecurrence.dayNumber.isEmpty() ) + writeString( e, "daynumber", mRecurrence.dayNumber ); + if ( !mRecurrence.month.isEmpty() ) + writeString( e, "month", mRecurrence.month ); + if ( !mRecurrence.rangeType.isEmpty() ) { + TQDomElement range = element.ownerDocument().createElement( "range" ); + e.appendChild( range ); + range.setAttribute( "type", mRecurrence.rangeType ); + TQDomText t = element.ownerDocument().createTextNode( mRecurrence.range ); + range.appendChild( t ); + } + for( TQValueList<TQDate>::ConstIterator it = mRecurrence.exclusions.begin(); + it != mRecurrence.exclusions.end(); ++it ) { + writeString( e, "exclusion", dateToString( *it ) ); + } +} + +void Incidence::loadRecurrence( const TQDomElement& element ) +{ + mRecurrence.interval = 0; + mRecurrence.cycle = element.attribute( "cycle" ); + mRecurrence.type = element.attribute( "type" ); + for ( TQDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) { + if ( n.isComment() ) + continue; + if ( n.isElement() ) { + TQDomElement e = n.toElement(); + TQString 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 + kdDebug() << "Warning: Unhandled tag " << e.tagName() << endl; + } + } +} + +static void loadAddressesHelper( const TQDomElement& element, KCal::Alarm* a ) +{ + for ( TQDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) { + if ( n.isComment() ) + continue; + if ( n.isElement() ) { + TQDomElement e = n.toElement(); + TQString tagName = e.tagName(); + + if ( tagName == "address" ) { + a->addMailAddress( KCal::Person( e.text() ) ); + } else { + kdWarning() << "Unhandled tag" << tagName << endl; + } + } + } +} + +static void loadAttachmentsHelper( const TQDomElement& element, KCal::Alarm* a ) +{ + for ( TQDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) { + if ( n.isComment() ) + continue; + if ( n.isElement() ) { + TQDomElement e = n.toElement(); + TQString tagName = e.tagName(); + + if ( tagName == "attachment" ) { + a->addMailAttachment( e.text() ); + } else { + kdWarning() << "Unhandled tag" << tagName << endl; + } + } + } +} + +static void loadAlarmHelper( const TQDomElement& element, KCal::Alarm* a ) +{ + for ( TQDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) { + if ( n.isComment() ) + continue; + if ( n.isElement() ) { + TQDomElement e = n.toElement(); + TQString 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 { + kdWarning() << "Unhandled tag" << tagName << endl; + } + } + } +} + +void Incidence::loadAlarms( const TQDomElement& element ) +{ + for ( TQDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) { + if ( n.isComment() ) + continue; + if ( n.isElement() ) { + TQDomElement e = n.toElement(); + TQString tagName = e.tagName(); + + if ( tagName == "alarm" ) { + KCal::Alarm *a = new KCal::Alarm( 0 ); + a->setEnabled( true ); // default to enabled, unless some XML attribute says otherwise. + TQString type = e.attribute( "type" ); + if ( type == "display" ) { + a->setType( KCal::Alarm::Display ); + } else if ( type == "procedure" ) { + a->setType( KCal::Alarm::Procedure ); + } else if ( type == "email" ) { + a->setType( KCal::Alarm::Email ); + } else if ( type == "audio" ) { + a->setType( KCal::Alarm::Audio ); + } else { + kdWarning() << "Unhandled alarm type:" << type << endl; + } + + loadAlarmHelper( e, a ); + mAlarms << a; + } else { + kdWarning() << "Unhandled tag" << tagName << endl; + } + } + } +} + +bool Incidence::loadAttribute( TQDomElement& element ) +{ + TQString 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( new KCal::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 { + bool ok = KolabBase::loadAttribute( element ); + if ( !ok ) { + // Unhandled tag - save for later storage + //kdDebug() << "Saving unhandled tag " << element.tagName() << endl; + Custom c; + c.key = TQCString( "X-TDE-KolabUnhandled-" ) + element.tagName().latin1(); + c.value = element.text(); + mCustomList.append( c ); + } + } + // We handled this + return true; +} + +bool Incidence::saveAttributes( TQDomElement& 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 = tqRound( -alarm() ); + writeString( element, "alarm", TQString::number( alarmTime ) ); + } + saveAlarms( element ); + writeString( element, "x-kde-internaluid", internalUID() ); + saveCustomAttributes( element ); + return true; +} + +void Incidence::saveCustomAttributes( TQDomElement& element ) const +{ + TQValueList<Custom>::ConstIterator it = mCustomList.begin(); + for ( ; it != mCustomList.end(); ++it ) { + TQString key = (*it).key; + Q_ASSERT( !key.isEmpty() ); + if ( key.startsWith( "X-TDE-KolabUnhandled-" ) ) { + key = key.mid( strlen( "X-TDE-KolabUnhandled-" ) ); + writeString( element, key, (*it).value ); + } else { + // Let's use attributes so that other tag-preserving-code doesn't need sub-elements + TQDomElement e = element.ownerDocument().createElement( "x-custom" ); + element.appendChild( e ); + e.setAttribute( "key", key ); + e.setAttribute( "value", (*it).value ); + } + } +} + +void Incidence::loadCustomAttributes( TQDomElement& element ) +{ + Custom custom; + custom.key = element.attribute( "key" ).latin1(); + custom.value = element.attribute( "value" ); + mCustomList.append( custom ); +} + +static KCal::Attendee::PartStat attendeeStringToStatus( const TQString& s ) +{ + if ( s == "none" ) + return KCal::Attendee::NeedsAction; + if ( s == "tentative" ) + return KCal::Attendee::Tentative; + if ( s == "accepted" ) + return KCal::Attendee::Accepted; + if ( s == "declined" ) + return KCal::Attendee::Declined; + if ( s == "delegated" ) + return KCal::Attendee::Delegated; + + // Default: + return KCal::Attendee::None; +} + +static TQString attendeeStatusToString( KCal::Attendee::PartStat status ) +{ + switch( status ) { + case KCal::Attendee::NeedsAction: + return "none"; + case KCal::Attendee::Accepted: + return "accepted"; + case KCal::Attendee::Declined: + return "declined"; + case KCal::Attendee::Tentative: + return "tentative"; + case KCal::Attendee::Delegated: + return "delegated"; + case KCal::Attendee::Completed: + case KCal::Attendee::InProcess: + // These don't have any meaning in the Kolab format, so just use: + return "accepted"; + } + + // Default for the case that there are more added later: + return "accepted"; +} + +static KCal::Attendee::Role attendeeStringToRole( const TQString& s ) +{ + if ( s == "optional" ) + return KCal::Attendee::OptParticipant; + if ( s == "resource" ) + return KCal::Attendee::NonParticipant; + return KCal::Attendee::ReqParticipant; +} + +static TQString attendeeRoleToString( KCal::Attendee::Role role ) +{ + switch( role ) { + case KCal::Attendee::ReqParticipant: + return "required"; + case KCal::Attendee::OptParticipant: + return "optional"; + case KCal::Attendee::Chair: + // We don't have the notion of chair, so use + return "required"; + case KCal::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( KCal::Recurrence* recur ) +{ + mRecurrence.interval = recur->frequency(); + switch ( recur->recurrenceType() ) { + case KCal::Recurrence::rMinutely: // Not handled by the kolab XML + mRecurrence.cycle = "minutely"; + break; + case KCal::Recurrence::rHourly: // Not handled by the kolab XML + mRecurrence.cycle = "hourly"; + break; + case KCal::Recurrence::rDaily: + mRecurrence.cycle = "daily"; + break; + case KCal::Recurrence::rWeekly: // every X weeks + mRecurrence.cycle = "weekly"; + { + TQBitArray arr = recur->days(); + for ( uint idx = 0 ; idx < 7 ; ++idx ) + if ( arr.testBit( idx ) ) + mRecurrence.days.append( s_weekDayName[idx] ); + } + break; + case KCal::Recurrence::rMonthlyPos: { + mRecurrence.cycle = "monthly"; + mRecurrence.type = "weekday"; + TQValueList<KCal::RecurrenceRule::WDayPos> monthPositions = recur->monthPositions(); + if ( !monthPositions.isEmpty() ) { + KCal::RecurrenceRule::WDayPos monthPos = monthPositions.first(); + // TODO: Handle multiple days in the same week + mRecurrence.dayNumber = TQString::number( monthPos.pos() ); + mRecurrence.days.append( s_weekDayName[ monthPos.day()-1 ] ); + // Not (properly) handled(?): monthPos.negative (nth days before end of month) + } + break; + } + case KCal::Recurrence::rMonthlyDay: { + mRecurrence.cycle = "monthly"; + mRecurrence.type = "daynumber"; + TQValueList<int> monthDays = recur->monthDays(); + // ####### Kolab XML limitation: only the first month day is used + if ( !monthDays.isEmpty() ) + mRecurrence.dayNumber = TQString::number( monthDays.first() ); + break; + } + case KCal::Recurrence::rYearlyMonth: // (day n of Month Y) + { + mRecurrence.cycle = "yearly"; + mRecurrence.type = "monthday"; + TQValueList<int> rmd = recur->yearDates(); + int day = !rmd.isEmpty() ? rmd.first() : recur->startDate().day(); + mRecurrence.dayNumber = TQString::number( day ); + TQValueList<int> months = recur->yearMonths(); + if ( !months.isEmpty() ) + mRecurrence.month = s_monthName[ months.first() - 1 ]; // #### Kolab XML limitation: only one month specified + break; + } + case KCal::Recurrence::rYearlyDay: // YearlyDay (day N of the year). Not supported by Outlook + mRecurrence.cycle = "yearly"; + mRecurrence.type = "yearday"; + mRecurrence.dayNumber = TQString::number( recur->yearDays().first() ); + break; + case KCal::Recurrence::rYearlyPos: // (weekday X of week N of month Y) + mRecurrence.cycle = "yearly"; + mRecurrence.type = "weekday"; + TQValueList<int> months = recur->yearMonths(); + if ( !months.isEmpty() ) + mRecurrence.month = s_monthName[ months.first() - 1 ]; // #### Kolab XML limitation: only one month specified + TQValueList<KCal::RecurrenceRule::WDayPos> monthPositions = recur->yearPositions(); + if ( !monthPositions.isEmpty() ) { + KCal::RecurrenceRule::WDayPos monthPos = monthPositions.first(); + // TODO: Handle multiple days in the same week + mRecurrence.dayNumber = TQString::number( monthPos.pos() ); + mRecurrence.days.append( s_weekDayName[ monthPos.day()-1 ] ); + + //mRecurrence.dayNumber = TQString::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 = TQString::number( howMany ); + } else if ( howMany == 0 ) { + mRecurrence.rangeType = "date"; + mRecurrence.range = dateToString( recur->endDate() ); + } else { + mRecurrence.rangeType = "none"; + } +} + +void Incidence::setFields( const KCal::Incidence* incidence ) +{ + KolabBase::setFields( incidence ); + + if ( incidence->doesFloat() ) { + // This is a floating 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->isAlarmEnabled() ) { + const KCal::Alarm::List& alarms = incidence->alarms(); + if ( !alarms.isEmpty() ) { + const KCal::Alarm* 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: + KCal::Attendee::List attendees = incidence->attendees(); + KCal::Attendee::List::ConstIterator it; + for ( it = attendees.begin(); it != attendees.end(); ++it ) { + KCal::Attendee* kcalAttendee = *it; + Attendee attendee; + + attendee.displayName = kcalAttendee->name(); + attendee.smtpAddress = kcalAttendee->email(); + attendee.status = attendeeStatusToString( kcalAttendee->status() ); + attendee.requestResponse = kcalAttendee->RSVP(); + // TODO: KCal::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 + KCal::Attachment::List attachments = incidence->attachments(); + KCal::Attachment::List::ConstIterator it2; + for ( it2 = attachments.begin(); it2 != attachments.end(); ++it2 ) { + KCal::Attachment *a = *it2; + mAttachments.push_back( a ); + } + + mAlarms.clear(); + + // Alarms + const KCal::Alarm::List alarms = incidence->alarms(); + for ( KCal::Alarm::List::ConstIterator it = alarms.begin(); it != alarms.end(); ++it ) { + mAlarms.push_back( *it ); + } + + if ( incidence->doesRecur() ) { + setRecurrence( incidence->recurrence() ); + mRecurrence.exclusions = incidence->recurrence()->exDates(); + } + + // Handle the scheduling ID + if ( incidence->schedulingID() == incidence->uid() ) { + // There is no scheduling ID + setInternalUID( TQString() ); + } 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() ); + } + + if ( incidence->pilotId() != 0 ) + setPilotSyncId( incidence->pilotId() ); + + setPilotSyncStatus( incidence->syncStatus() ); + + // Unhandled tags and other custom properties (see libkcal/customproperties.h) + const TQMap<TQCString, TQString> map = incidence->customProperties(); + TQMap<TQCString, TQString>::ConstIterator cit = map.begin(); + for ( ; cit != map.end() ; ++cit ) { + Custom c; + c.key = cit.key(); + c.value = cit.data(); + mCustomList.append( c ); + } +} + +static TQBitArray daysListToBitArray( const TQStringList& days ) +{ + TQBitArray arr( 7 ); + arr.fill( false ); + for( TQStringList::ConstIterator it = days.begin(); it != days.end(); ++it ) { + for ( uint i = 0; i < 7 ; ++i ) + if ( *it == s_weekDayName[i] ) + arr.setBit( i, true ); + } + return arr; +} + + +void Incidence::saveTo( KCal::Incidence* incidence ) +{ + KolabBase::saveTo( incidence ); + + if ( mFloatingStatus == AllDay ) { + // This is a floating event. Don't timezone move this one + incidence->setDtStart( startDate() ); + incidence->setFloats( true ); + } else { + incidence->setDtStart( utcToLocal( startDate() ) ); + incidence->setFloats( false ); + } + + incidence->setSummary( summary() ); + incidence->setLocation( location() ); + + if ( mHasAlarm && mAlarms.isEmpty() ) { + KCal::Alarm* alarm = incidence->newAlarm(); + alarm->setStartOffset( tqRound( mAlarm * 60.0 ) ); + alarm->setEnabled( true ); + alarm->setType( KCal::Alarm::Display ); + } else if ( !mAlarms.isEmpty() ) { + for ( KCal::Alarm::List::ConstIterator it = mAlarms.constBegin(); it != mAlarms.constEnd(); ++it ) { + KCal::Alarm *alarm = *it; + alarm->setParent( incidence ); + incidence->addAlarm( alarm ); + } + } + + if ( organizer().displayName.isEmpty() ) + incidence->setOrganizer( organizer().smtpAddress ); + else + incidence->setOrganizer( organizer().displayName + "<" + + organizer().smtpAddress + ">" ); + + incidence->clearAttendees(); + TQValueList<Attendee>::ConstIterator it; + for ( it = mAttendees.begin(); it != mAttendees.end(); ++it ) { + KCal::Attendee::PartStat status = attendeeStringToStatus( (*it).status ); + KCal::Attendee::Role role = attendeeStringToRole( (*it).role ); + KCal::Attendee* attendee = new KCal::Attendee( (*it).displayName, + (*it).smtpAddress, + (*it).requestResponse, + status, role ); + attendee->setDelegate( (*it).delegate ); + attendee->setDelegator( (*it).delegator ); + incidence->addAttendee( attendee ); + } + + incidence->clearAttachments(); + KCal::Attachment::List::ConstIterator it2; + for ( it2 = mAttachments.begin(); it2 != mAttachments.end(); ++it2 ) { + KCal::Attachment *a = (*it2); + // TODO should we copy? + incidence->addAttachment( a ); + } + + if ( !mRecurrence.cycle.isEmpty() ) { + KCal::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" ) { + TQBitArray 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 kdWarning() << "Unhandled monthly recurrence type " << mRecurrence.type << endl; + } 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 kdWarning() << "Unhandled yearly recurrence type " << mRecurrence.type << endl; + } else kdWarning() << "Unhandled recurrence cycle " << mRecurrence.cycle << endl; + + 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() ); + } + if ( hasPilotSyncId() ) + incidence->setPilotId( pilotSyncId() ); + if ( hasPilotSyncStatus() ) + incidence->setSyncStatus( pilotSyncStatus() ); + + for( TQValueList<Custom>::ConstIterator it = mCustomList.constBegin(); it != mCustomList.constEnd(); ++it ) { + incidence->setNonKDECustomProperty( (*it).key, (*it).value ); + } + +} + +void Incidence::loadAttachments() +{ + TQStringList attachments; + if ( mResource->kmailListAttachments( attachments, mSubResource, mSernum ) ) { + for ( TQStringList::ConstIterator it = attachments.constBegin(); it != attachments.constEnd(); ++it ) { + TQByteArray data; + KURL url; + if ( mResource->kmailGetAttachment( url, mSubResource, mSernum, *it ) && !url.isEmpty() ) { + TQFile f( url.path() ); + if ( f.open( IO_ReadOnly ) ) { + data = f.readAll(); + TQString mimeType; + if ( !mResource->kmailAttachmentMimetype( mimeType, mSubResource, mSernum, *it ) ) + mimeType = "application/octet-stream"; + KCal::Attachment *a = new KCal::Attachment( KCodecs::base64Encode( data ).data(), mimeType ); + a->setLabel( *it ); + mAttachments.append( a ); + f.close(); + } + f.remove(); + } + } + } +} + +TQString Incidence::productID() const +{ + return TQString( "KOrganizer %1, Kolab resource" ).arg( korgVersion ); +} + +// Unhandled KCal::Incidence fields: +// revision, status (unused), priority (done in tasks), attendee.uid, +// mComments, mReadOnly + |