00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "incidenceformatter.h"
00024
00025 #include <libkcal/attachment.h>
00026 #include <libkcal/event.h>
00027 #include <libkcal/todo.h>
00028 #include <libkcal/journal.h>
00029 #include <libkcal/calendar.h>
00030 #include <libkcal/calendarlocal.h>
00031 #include <libkcal/icalformat.h>
00032 #include <libkcal/freebusy.h>
00033 #include <libkcal/calendarresources.h>
00034
00035 #include <libemailfunctions/email.h>
00036
00037 #include <ktnef/ktnefparser.h>
00038 #include <ktnef/ktnefmessage.h>
00039 #include <ktnef/ktnefdefs.h>
00040 #include <kabc/phonenumber.h>
00041 #include <kabc/vcardconverter.h>
00042 #include <kabc/stdaddressbook.h>
00043
00044 #include <kapplication.h>
00045 #include <kemailsettings.h>
00046
00047
00048 #include <klocale.h>
00049 #include <kglobal.h>
00050 #include <kiconloader.h>
00051 #include <kcalendarsystem.h>
00052 #include <kmimetype.h>
00053
00054 #include <qbuffer.h>
00055 #include <qstylesheet.h>
00056 #include <qdatetime.h>
00057
00058 #include <time.h>
00059
00060 using namespace KCal;
00061
00062
00063
00064
00065
00066
00067 static QString eventViewerAddLink( const QString &ref, const QString &text,
00068 bool newline = true )
00069 {
00070 QString tmpStr( "<a href=\"" + ref + "\">" + text + "</a>" );
00071 if ( newline ) tmpStr += "\n";
00072 return tmpStr;
00073 }
00074
00075 static QString eventViewerAddTag( const QString & tag, const QString & text )
00076 {
00077 int numLineBreaks = text.contains( "\n" );
00078 QString str = "<" + tag + ">";
00079 QString tmpText = text;
00080 QString tmpStr = str;
00081 if( numLineBreaks >= 0 ) {
00082 if ( numLineBreaks > 0) {
00083 int pos = 0;
00084 QString tmp;
00085 for( int i = 0; i <= numLineBreaks; i++ ) {
00086 pos = tmpText.find( "\n" );
00087 tmp = tmpText.left( pos );
00088 tmpText = tmpText.right( tmpText.length() - pos - 1 );
00089 tmpStr += tmp + "<br>";
00090 }
00091 } else {
00092 tmpStr += tmpText;
00093 }
00094 }
00095 tmpStr += "</" + tag + ">";
00096 return tmpStr;
00097 }
00098
00099 static QString linkPerson( const QString& email, QString name, QString uid )
00100 {
00101
00102
00103 if ( !email.isEmpty() && ( name.isEmpty() || uid.isEmpty() ) ) {
00104 KABC::AddressBook *add_book = KABC::StdAddressBook::self( true );
00105 KABC::Addressee::List addressList = add_book->findByEmail( email );
00106 if ( !addressList.isEmpty() ) {
00107 KABC::Addressee o = addressList.first();
00108 if ( !o.isEmpty() && addressList.size() < 2 ) {
00109 if ( name.isEmpty() ) {
00110
00111 name = o.formattedName();
00112 }
00113 uid = o.uid();
00114 } else {
00115
00116 uid = QString::null;
00117 }
00118 }
00119 }
00120 kdDebug(5850) << "formatAttendees: uid = " << uid << endl;
00121
00122
00123 QString tmpString = "<li>";
00124 if ( !uid.isEmpty() ) {
00125
00126 if ( name.isEmpty() ) {
00127
00128 tmpString += eventViewerAddLink( "uid:" + uid, email );
00129 } else {
00130 tmpString += eventViewerAddLink( "uid:" + uid, name );
00131 }
00132 } else {
00133
00134 tmpString += ( name.isEmpty() ? email : name );
00135 }
00136 tmpString += '\n';
00137
00138
00139 if ( !email.isEmpty() ) {
00140 KURL mailto;
00141 mailto.setProtocol( "mailto" );
00142 mailto.setPath( email );
00143 const QString iconPath =
00144 KGlobal::iconLoader()->iconPath( "mail_new", KIcon::Small );
00145 tmpString += eventViewerAddLink( mailto.url(), "<img src=\"" + iconPath + "\">" );
00146 }
00147 tmpString += "</li>";
00148
00149 return tmpString;
00150 }
00151
00152 static QString eventViewerFormatAttendees( Incidence *event )
00153 {
00154 QString tmpStr;
00155 Attendee::List attendees = event->attendees();
00156 if ( attendees.count() ) {
00157
00158
00159 tmpStr += eventViewerAddTag( "i", i18n("Organizer") );
00160 tmpStr += "<ul>";
00161 tmpStr += linkPerson( event->organizer().email(),
00162 event->organizer().name(), QString::null );
00163 tmpStr += "</ul>";
00164
00165
00166 tmpStr += eventViewerAddTag( "i", i18n("Attendees") );
00167 tmpStr += "<ul>";
00168 Attendee::List::ConstIterator it;
00169 for( it = attendees.begin(); it != attendees.end(); ++it ) {
00170 Attendee *a = *it;
00171 tmpStr += linkPerson( a->email(), a->name(), a->uid() );
00172 if ( !a->delegator().isEmpty() ) {
00173 tmpStr += i18n(" (delegated by %1)" ).arg( a->delegator() );
00174 }
00175 if ( !a->delegate().isEmpty() ) {
00176 tmpStr += i18n(" (delegated to %1)" ).arg( a->delegate() );
00177 }
00178 }
00179 tmpStr += "</ul>";
00180 }
00181 return tmpStr;
00182 }
00183
00184 static QString eventViewerFormatAttachments( Incidence *i )
00185 {
00186 QString tmpStr;
00187 Attachment::List as = i->attachments();
00188 if ( as.count() > 0 ) {
00189 Attachment::List::ConstIterator it;
00190 for( it = as.begin(); it != as.end(); ++it ) {
00191 if ( (*it)->isUri() ) {
00192 QString name;
00193 if ( (*it)->uri().startsWith( "kmail:" ) )
00194 name = i18n( "Show mail" );
00195 else
00196 name = (*it)->uri();
00197 tmpStr += eventViewerAddLink( (*it)->uri(), name );
00198 tmpStr += "<br>";
00199 }
00200 }
00201 }
00202 return tmpStr;
00203 }
00204
00205
00206
00207
00208
00209 static QString eventViewerFormatBirthday( Event *event )
00210 {
00211 if ( !event) return QString::null;
00212 if ( event->customProperty("KABC","BIRTHDAY") != "YES" ) return QString::null;
00213
00214 QString uid = event->customProperty("KABC","UID-1");
00215 QString name = event->customProperty("KABC","NAME-1");
00216 QString email= event->customProperty("KABC","EMAIL-1");
00217
00218 QString tmpString = "<ul>";
00219 tmpString += linkPerson( email, name, uid );
00220
00221 if ( event->customProperty( "KABC", "ANNIVERSARY") == "YES" ) {
00222 uid = event->customProperty("KABC","UID-2");
00223 name = event->customProperty("KABC","NAME-2");
00224 email= event->customProperty("KABC","EMAIL-2");
00225 tmpString += linkPerson( email, name, uid );
00226 }
00227
00228 tmpString += "</ul>";
00229 return tmpString;
00230 }
00231
00232 static QString eventViewerFormatHeader( Incidence *incidence )
00233 {
00234 QString tmpStr = "<table><tr>";
00235
00236
00237 {
00238 tmpStr += "<td>";
00239
00240 if ( incidence->type() == "Event" ) {
00241 tmpStr += "<img src=\"" +
00242 KGlobal::iconLoader()->iconPath( "appointment", KIcon::Small ) +
00243 "\">";
00244 }
00245 if ( incidence->type() == "Todo" ) {
00246 tmpStr += "<img src=\"" +
00247 KGlobal::iconLoader()->iconPath( "todo", KIcon::Small ) +
00248 "\">";
00249 }
00250 if ( incidence->type() == "Journal" ) {
00251 tmpStr += "<img src=\"" +
00252 KGlobal::iconLoader()->iconPath( "journal", KIcon::Small ) +
00253 "\">";
00254 }
00255 if ( incidence->isAlarmEnabled() ) {
00256 tmpStr += "<img src=\"" +
00257 KGlobal::iconLoader()->iconPath( "bell", KIcon::Small ) +
00258 "\">";
00259 }
00260 if ( incidence->doesRecur() ) {
00261 tmpStr += "<img src=\"" +
00262 KGlobal::iconLoader()->iconPath( "recur", KIcon::Small ) +
00263 "\">";
00264 }
00265 if ( incidence->isReadOnly() ) {
00266 tmpStr += "<img src=\"" +
00267 KGlobal::iconLoader()->iconPath( "readonlyevent", KIcon::Small ) +
00268 "\">";
00269 }
00270
00271 tmpStr += "</td>";
00272 }
00273
00274 tmpStr += "<td>"
00275 + eventViewerAddTag( "u",
00276 eventViewerAddTag( "b", incidence->summary() ) )
00277 + "</td>";
00278 tmpStr += "</tr></table><br>";
00279
00280 return tmpStr;
00281 }
00282
00283 static QString eventViewerFormatEvent( Event *event )
00284 {
00285 if ( !event ) return QString::null;
00286 QString tmpStr = eventViewerFormatHeader( event );
00287
00288 tmpStr += "<table>";
00289
00290 tmpStr += "<tr>";
00291 if ( event->doesFloat() ) {
00292 if ( event->isMultiDay() ) {
00293 tmpStr += "<td align=\"right\"><b>" + i18n( "Time" ) + "</b></td>";
00294 tmpStr += "<td>" + i18n("<beginTime> - <endTime>","%1 - %2")
00295 .arg( event->dtStartDateStr() )
00296 .arg( event->dtEndDateStr() ) + "</td>";
00297 } else {
00298 tmpStr += "<td align=\"right\"><b>" + i18n( "Date" ) + "</b></td>";
00299 tmpStr += "<td>" + i18n("date as string","%1").arg( event->dtStartDateStr() ) + "</td>";
00300 }
00301 } else {
00302 if ( event->isMultiDay() ) {
00303 tmpStr += "<td align=\"right\"><b>" + i18n( "Time" ) + "</b></td>";
00304 tmpStr += "<td>" + i18n("<beginTime> - <endTime>","%1 - %2")
00305 .arg( event->dtStartStr() )
00306 .arg( event->dtEndStr() ) + "</td>";
00307 } else {
00308 tmpStr += "<td align=\"right\"><b>" + i18n( "Time" ) + "</b></td>";
00309 if ( event->hasEndDate() && event->dtStart() != event->dtEnd()) {
00310 tmpStr += "<td>" + i18n("<beginTime> - <endTime>","%1 - %2")
00311 .arg( event->dtStartTimeStr() )
00312 .arg( event->dtEndTimeStr() ) + "</td>";
00313 } else {
00314 tmpStr += "<td>" + event->dtStartTimeStr() + "</td>";
00315 }
00316 tmpStr += "</tr><tr>";
00317 tmpStr += "<td align=\"right\"><b>" + i18n( "Date" ) + "</b></td>";
00318 tmpStr += "<td>" + i18n("date as string","%1")
00319 .arg( event->dtStartDateStr() ) + "</td>";
00320 }
00321 }
00322 tmpStr += "</tr>";
00323
00324 if ( event->customProperty("KABC","BIRTHDAY")== "YES" ) {
00325 tmpStr += "<tr>";
00326 if ( event->customProperty( "KABC", "ANNIVERSARY" ) == "YES" ) {
00327 tmpStr += "<td align=\"right\"><b>" + i18n( "Anniversary" ) + "</b></td>";
00328 } else {
00329 tmpStr += "<td align=\"right\"><b>" + i18n( "Birthday" ) + "</b></td>";
00330 }
00331 tmpStr += "<td>" + eventViewerFormatBirthday( event ) + "</td>";
00332 tmpStr += "</tr>";
00333 tmpStr += "</table>";
00334 return tmpStr;
00335 }
00336
00337 if ( !event->description().isEmpty() ) {
00338 tmpStr += "<tr>";
00339 tmpStr += "<td align=\"right\"><b>" + i18n( "Description" ) + "</b></td>";
00340 tmpStr += "<td>" + eventViewerAddTag( "p", event->description() ) + "</td>";
00341 tmpStr += "</tr>";
00342 }
00343
00344 if ( !event->location().isEmpty() ) {
00345 tmpStr += "<tr>";
00346 tmpStr += "<td align=\"right\"><b>" + i18n( "Location" ) + "</b></td>";
00347 tmpStr += "<td>" + event->location() + "</td>";
00348 tmpStr += "</tr>";
00349 }
00350
00351 if ( event->categories().count() > 0 ) {
00352 tmpStr += "<tr>";
00353 tmpStr += "<td align=\"right\"><b>" + i18n( "1 Category", "%n Categories", event->categories().count() )+ "</b></td>";
00354 tmpStr += "<td>" + event->categoriesStr() + "</td>";
00355 tmpStr += "</tr>";
00356 }
00357
00358 if ( event->doesRecur() ) {
00359 QDateTime dt =
00360 event->recurrence()->getNextDateTime( QDateTime::currentDateTime() );
00361 tmpStr += "<tr>";
00362 tmpStr += "<td align=\"right\"><b>" + i18n( "Next on" ) + "</b></td>";
00363 if ( !event->doesFloat() ) {
00364 tmpStr += "<td>" +
00365 KGlobal::locale()->formatDateTime( dt, true ) + "</td>";
00366 } else {
00367 tmpStr += "<td>" +
00368 KGlobal::locale()->formatDate( dt.date(), true ) + "</td>";
00369 }
00370 tmpStr += "</tr>";
00371 }
00372
00373 int attendeeCount = event->attendees().count();
00374 if ( attendeeCount > 0 ) {
00375 tmpStr += "<tr><td colspan=\"2\">";
00376 tmpStr += eventViewerFormatAttendees( event );
00377 tmpStr += "</td></tr>";
00378 }
00379
00380 int attachmentCount = event->attachments().count();
00381 if ( attachmentCount > 0 ) {
00382 tmpStr += "<tr>";
00383 tmpStr += "<td align=\"right\"><b>" + i18n( "1 attachment", "%n attachments", attachmentCount )+ "</b></td>";
00384 tmpStr += "<td>" + eventViewerFormatAttachments( event ) + "</td>";
00385 tmpStr += "</tr>";
00386 }
00387
00388 tmpStr += "</table>";
00389 tmpStr += "<em>" + i18n( "Creation date: %1.").arg(
00390 KGlobal::locale()->formatDateTime( event->created() , true ) ) + "</em>";
00391 return tmpStr;
00392 }
00393
00394 static QString eventViewerFormatTodo( Todo *todo )
00395 {
00396 if ( !todo ) return QString::null;
00397 QString tmpStr = eventViewerFormatHeader( todo );
00398
00399 tmpStr += "<table>";
00400
00401 if ( todo->hasDueDate() ) {
00402 tmpStr += "<tr>";
00403 tmpStr += "<td align=\"right\"><b>" + i18n( "Due on" ) + "</b></td>";
00404 if ( !todo->doesFloat() ) {
00405 tmpStr += "<td>" +
00406 KGlobal::locale()->formatDateTime( todo->dtDue(), true ) +
00407 "</td>";
00408 } else {
00409 tmpStr += "<td>" +
00410 KGlobal::locale()->formatDate( todo->dtDue().date(), true ) +
00411 "</td>";
00412 }
00413 tmpStr += "</tr>";
00414 }
00415
00416 if ( !todo->description().isEmpty() ) {
00417 tmpStr += "<tr>";
00418 tmpStr += "<td align=\"right\"><b>" + i18n( "Description" ) + "</b></td>";
00419 tmpStr += "<td>" + todo->description() + "</td>";
00420 tmpStr += "</tr>";
00421 }
00422
00423 if ( !todo->location().isEmpty() ) {
00424 tmpStr += "<tr>";
00425 tmpStr += "<td align=\"right\"><b>" + i18n( "Location" ) + "</b></td>";
00426 tmpStr += "<td>" + todo->location() + "</td>";
00427 tmpStr += "</tr>";
00428 }
00429
00430 if ( todo->categories().count() > 0 ) {
00431 tmpStr += "<tr>";
00432 tmpStr += "<td align=\"right\"><b>" + i18n( "1 Category", "%n Categories", todo->categories().count() )+ "</b></td>";
00433 tmpStr += "<td>" + todo->categoriesStr() + "</td>";
00434 tmpStr += "</tr>";
00435 }
00436
00437 tmpStr += "<tr>";
00438 tmpStr += "<td align=\"right\"><b>" + i18n( "Priority" ) + "</b></td>";
00439 if ( todo->priority() > 0 ) {
00440 tmpStr += "<td>" + QString::number( todo->priority() ) + "</td>";
00441 } else {
00442 tmpStr += "<td>" + i18n( "Unspecified" ) + "</td>";
00443 }
00444 tmpStr += "</tr>";
00445
00446 tmpStr += "<tr>";
00447 tmpStr += "<td align=\"right\"><b>" + i18n( "Completed" ) + "</b></td>";
00448 tmpStr += "<td>" + i18n( "%1 %" ).arg( todo->percentComplete() ) + "</td>";
00449 tmpStr += "</tr>";
00450
00451 if ( todo->doesRecur() ) {
00452 QDateTime dt =
00453 todo->recurrence()->getNextDateTime( QDateTime::currentDateTime() );
00454 tmpStr += "<tr>";
00455 tmpStr += "<td align=\"right\"><b>" + i18n( "Next on" ) + "</b></td>";
00456 if ( !todo->doesFloat() ) {
00457 tmpStr += "<td>" +
00458 KGlobal::locale()->formatDateTime( dt, true ) + "</td>";
00459 } else {
00460 tmpStr += "<td>" +
00461 KGlobal::locale()->formatDate( dt.date(), true ) + "</td>";
00462 }
00463 tmpStr += "</tr>";
00464 }
00465
00466 int attendeeCount = todo->attendees().count();
00467 if ( attendeeCount > 0 ) {
00468 tmpStr += "<tr><td colspan=\"2\">";
00469 tmpStr += eventViewerFormatAttendees( todo );
00470 tmpStr += "</td></tr>";
00471 }
00472
00473 int attachmentCount = todo->attachments().count();
00474 if ( attachmentCount > 0 ) {
00475 tmpStr += "<tr>";
00476 tmpStr += "<td align=\"right\"><b>" + i18n( "1 attachment", "%n attachments", attachmentCount )+ "</b></td>";
00477 tmpStr += "<td>" + eventViewerFormatAttachments( todo ) + "</td>";
00478 tmpStr += "</tr>";
00479 }
00480
00481 tmpStr += "</table>";
00482 tmpStr += "<em>" + i18n( "Creation date: %1.").arg(
00483 KGlobal::locale()->formatDateTime( todo->created(), true ) ) + "</em>";
00484 return tmpStr;
00485 }
00486
00487 static QString eventViewerFormatJournal( Journal *journal )
00488 {
00489 if ( !journal ) return QString::null;
00490
00491 QString tmpStr;
00492 if ( !journal->summary().isEmpty() ) {
00493 tmpStr += eventViewerAddTag( "u",
00494 eventViewerAddTag( "b", journal->summary() ) );
00495 }
00496 tmpStr += eventViewerAddTag( "b", i18n("Journal for %1").arg( journal->dtStartDateStr( false ) ) );
00497 if ( !journal->description().isEmpty() )
00498 tmpStr += eventViewerAddTag( "p", journal->description() );
00499 return tmpStr;
00500 }
00501
00502 static QString eventViewerFormatFreeBusy( FreeBusy *fb )
00503 {
00504 if ( !fb ) return QString::null;
00505
00506 QString tmpStr =
00507 eventViewerAddTag( "u",
00508 eventViewerAddTag( "b", i18n("Free/Busy information for %1")
00509 .arg( fb->organizer().fullName() ) ) );
00510 tmpStr += eventViewerAddTag( "i", i18n("Busy times in date range %1 - %2:")
00511 .arg( KGlobal::locale()->formatDate( fb->dtStart().date(), true ) )
00512 .arg( KGlobal::locale()->formatDate( fb->dtEnd().date(), true ) ) );
00513
00514 QValueList<Period> periods = fb->busyPeriods();
00515
00516 QString text = eventViewerAddTag( "em", eventViewerAddTag( "b", i18n("Busy:") ) );
00517 QValueList<Period>::iterator it;
00518 for ( it = periods.begin(); it != periods.end(); ++it ) {
00519 Period per = *it;
00520 if ( per.hasDuration() ) {
00521 int dur = per.duration().asSeconds();
00522 QString cont;
00523 if ( dur >= 3600 ) {
00524 cont += i18n("1 hour ", "%n hours ", dur / 3600 );
00525 dur %= 3600;
00526 }
00527 if ( dur >= 60 ) {
00528 cont += i18n("1 minute ", "%n minutes ", dur / 60);
00529 dur %= 60;
00530 }
00531 if ( dur > 0 ) {
00532 cont += i18n("1 second", "%n seconds", dur);
00533 }
00534 text += i18n("startDate for duration", "%1 for %2")
00535 .arg( KGlobal::locale()->formatDateTime( per.start(), false ) )
00536 .arg( cont );
00537 text += "<br>";
00538 } else {
00539 if ( per.start().date() == per.end().date() ) {
00540 text += i18n("date, fromTime - toTime ", "%1, %2 - %3")
00541 .arg( KGlobal::locale()->formatDate( per.start().date() ) )
00542 .arg( KGlobal::locale()->formatTime( per.start().time() ) )
00543 .arg( KGlobal::locale()->formatTime( per.end().time() ) );
00544 } else {
00545 text += i18n("fromDateTime - toDateTime", "%1 - %2")
00546 .arg( KGlobal::locale()->formatDateTime( per.start(), false ) )
00547 .arg( KGlobal::locale()->formatDateTime( per.end(), false ) );
00548 }
00549 text += "<br>";
00550 }
00551 }
00552 tmpStr += eventViewerAddTag( "p", text );
00553 return tmpStr;
00554 }
00555
00556 class IncidenceFormatter::EventViewerVisitor : public IncidenceBase::Visitor
00557 {
00558 public:
00559 EventViewerVisitor() { mResult = ""; }
00560 bool act( IncidenceBase *incidence ) { return incidence->accept( *this ); }
00561 QString result() const { return mResult; }
00562 protected:
00563 bool visit( Event *event )
00564 {
00565 mResult = eventViewerFormatEvent( event );
00566 return !mResult.isEmpty();
00567 }
00568 bool visit( Todo *todo )
00569 {
00570 mResult = eventViewerFormatTodo( todo );
00571 return !mResult.isEmpty();
00572 }
00573 bool visit( Journal *journal )
00574 {
00575 mResult = eventViewerFormatJournal( journal );
00576 return !mResult.isEmpty();
00577 }
00578 bool visit( FreeBusy *fb )
00579 {
00580 mResult = eventViewerFormatFreeBusy( fb );
00581 return !mResult.isEmpty();
00582 }
00583
00584 protected:
00585 QString mResult;
00586 };
00587
00588 QString IncidenceFormatter::extensiveDisplayString( IncidenceBase *incidence )
00589 {
00590 if ( !incidence ) return QString::null;
00591 EventViewerVisitor v;
00592 if ( v.act( incidence ) ) {
00593 return v.result();
00594 } else
00595 return QString::null;
00596 }
00597
00598
00599
00600
00601
00602
00603
00604
00605 static QString string2HTML( const QString& str )
00606 {
00607 return QStyleSheet::convertFromPlainText(str, QStyleSheetItem::WhiteSpaceNormal);
00608 }
00609
00610 static QString eventStartTimeStr( Event *event )
00611 {
00612 QString tmp;
00613 if ( !event->doesFloat() ) {
00614 tmp = i18n( "%1: Start Date, %2: Start Time", "%1 %2" ).
00615 arg( event->dtStartDateStr( false ), event->dtStartTimeStr() );
00616 } else {
00617 tmp = i18n( "%1: Start Date", "%1" ).
00618 arg( event->dtStartDateStr( false ) );
00619 }
00620 return tmp;
00621 }
00622
00623 static QString eventEndTimeStr( Event *event )
00624 {
00625 QString tmp;
00626 if ( event->hasEndDate() && event->dtEnd().isValid() ) {
00627 if ( !event->doesFloat() ) {
00628 tmp = i18n( "%1: End Date, %2: End Time", "%1 %2" ).
00629 arg( event->dtEndDateStr( false ), event->dtEndTimeStr() );
00630 } else {
00631 tmp = i18n( "%1: End Date", "%1" ).arg( event->dtEndDateStr( false ) );
00632 }
00633 }
00634 return tmp;
00635 }
00636
00637 static QString invitationRow( const QString &cell1, const QString &cell2 )
00638 {
00639 return "<tr><td>" + cell1 + "</td><td>" + cell2 + "</td></tr>\n";
00640 }
00641
00642 static bool iamOrganizer( Incidence *incidence )
00643 {
00644
00645
00646 if ( !incidence ) {
00647 return false;
00648 }
00649
00650 bool iam = false;
00651 KEMailSettings settings;
00652 QStringList profiles = settings.profiles();
00653 for( QStringList::Iterator it=profiles.begin(); it!=profiles.end(); ++it ) {
00654 settings.setProfile( *it );
00655 if ( settings.getSetting( KEMailSettings::EmailAddress ) == incidence->organizer().email() ) {
00656 iam = true;
00657 break;
00658 }
00659 }
00660 return iam;
00661 }
00662
00663 static bool iamAttendee( Attendee *attendee )
00664 {
00665
00666
00667 bool iam = false;
00668 KEMailSettings settings;
00669 QStringList profiles = settings.profiles();
00670 for( QStringList::Iterator it=profiles.begin(); it!=profiles.end(); ++it ) {
00671 settings.setProfile( *it );
00672 if ( settings.getSetting( KEMailSettings::EmailAddress ) == attendee->email() ) {
00673 iam = true;
00674 break;
00675 }
00676 }
00677 return iam;
00678 }
00679
00680 static Attendee *findMyAttendee( Incidence *incidence )
00681 {
00682
00683
00684 Attendee *attendee = 0;
00685 if ( !incidence ) {
00686 return attendee;
00687 }
00688
00689 KEMailSettings settings;
00690 QStringList profiles = settings.profiles();
00691 for( QStringList::Iterator it=profiles.begin(); it!=profiles.end(); ++it ) {
00692 settings.setProfile( *it );
00693
00694 Attendee::List attendees = incidence->attendees();
00695 Attendee::List::ConstIterator it2;
00696 for ( it2 = attendees.begin(); it2 != attendees.end(); ++it2 ) {
00697 Attendee *a = *it2;
00698 if ( settings.getSetting( KEMailSettings::EmailAddress ) == a->email() ) {
00699 attendee = a;
00700 break;
00701 }
00702 }
00703 }
00704 return attendee;
00705 }
00706
00707 static Attendee *findAttendee( Incidence *incidence, const QString &email )
00708 {
00709
00710
00711 Attendee *attendee = 0;
00712 if ( !incidence ) {
00713 return attendee;
00714 }
00715
00716 Attendee::List attendees = incidence->attendees();
00717 Attendee::List::ConstIterator it;
00718 for ( it = attendees.begin(); it != attendees.end(); ++it ) {
00719 Attendee *a = *it;
00720 if ( email == a->email() ) {
00721 attendee = a;
00722 break;
00723 }
00724 }
00725 return attendee;
00726 }
00727
00728 static bool rsvpRequested( Incidence *incidence )
00729 {
00730 if ( !incidence ) {
00731 return false;
00732 }
00733
00734
00735
00736 bool rsvp = true;
00737 Attendee::List attendees = incidence->attendees();
00738 Attendee::List::ConstIterator it;
00739 for ( it = attendees.begin(); it != attendees.end(); ++it ) {
00740 if ( it == attendees.begin() ) {
00741 rsvp = (*it)->RSVP();
00742 } else {
00743 if ( (*it)->RSVP() != rsvp ) {
00744 rsvp = true;
00745 break;
00746 }
00747 }
00748 }
00749 return rsvp;
00750 }
00751
00752 static QString rsvpRequestedStr( bool rsvpRequested )
00753 {
00754 if ( rsvpRequested ) {
00755 return i18n( "Your response is requested" );
00756 } else {
00757 return i18n( "A response is not necessary" );
00758 }
00759 }
00760
00761 static QString invitationPerson( const QString& email, QString name, QString uid )
00762 {
00763
00764
00765 if ( !email.isEmpty() && ( name.isEmpty() || uid.isEmpty() ) ) {
00766 KABC::AddressBook *add_book = KABC::StdAddressBook::self( true );
00767 KABC::Addressee::List addressList = add_book->findByEmail( email );
00768 if ( !addressList.isEmpty() ) {
00769 KABC::Addressee o = addressList.first();
00770 if ( !o.isEmpty() && addressList.size() < 2 ) {
00771 if ( name.isEmpty() ) {
00772
00773 name = o.formattedName();
00774 }
00775 uid = o.uid();
00776 } else {
00777
00778 uid = QString::null;
00779 }
00780 }
00781 }
00782
00783
00784 QString tmpString;
00785 if ( !uid.isEmpty() ) {
00786
00787 if ( name.isEmpty() ) {
00788
00789 tmpString += eventViewerAddLink( "uid:" + uid, email );
00790 } else {
00791 tmpString += eventViewerAddLink( "uid:" + uid, name );
00792 }
00793 } else {
00794
00795 tmpString += ( name.isEmpty() ? email : name );
00796 }
00797 tmpString += '\n';
00798
00799
00800 if ( !email.isEmpty() ) {
00801 KCal::Person person( name, email );
00802 KURL mailto;
00803 mailto.setProtocol( "mailto" );
00804 mailto.setPath( person.fullName() );
00805 const QString iconPath =
00806 KGlobal::iconLoader()->iconPath( "mail_new", KIcon::Small );
00807 tmpString += eventViewerAddLink( mailto.url(), "<img src=\"" + iconPath + "\">" )
00808 ;
00809 }
00810 tmpString += "\n";
00811
00812 return tmpString;
00813 }
00814
00815 static QString invitationsDetailsIncidence( Incidence *incidence )
00816 {
00817 QString html;
00818 QString descr;
00819 QStringList comments;
00820 if ( incidence->comments().isEmpty() && !incidence->description().isEmpty() ) {
00821 comments << incidence->description();
00822 } else {
00823 descr = incidence->description();
00824 comments = incidence->comments();
00825 }
00826
00827 if( !descr.isEmpty() ) {
00828 html += "<br/><u>" + i18n("Description:")
00829 + "</u><table border=\"0\"><tr><td> </td><td>";
00830 html += string2HTML(descr) + "</td></tr></table>";
00831 }
00832 if ( !comments.isEmpty() ) {
00833 html += "<br><u>" + i18n("Comments:")
00834 + "</u><table border=\"0\"><tr><td> </td><td>";
00835 if ( comments.count() > 1 ) {
00836 html += "<ul>";
00837 for ( uint i = 0; i < comments.count(); ++i )
00838 html += "<li>" + string2HTML( comments[i] ) + "</li>";
00839 html += "</ul>";
00840 } else {
00841 html += string2HTML( comments[0] );
00842 }
00843 html += "</td></tr></table>";
00844 }
00845 return html;
00846 }
00847
00848 static QString invitationDetailsEvent( Event* event )
00849 {
00850
00851 if ( !event )
00852 return QString::null;
00853
00854 QString html;
00855 QString tmp;
00856
00857 QString sSummary = i18n( "Summary unspecified" );
00858 if ( ! event->summary().isEmpty() ) {
00859 sSummary = QStyleSheet::escape( event->summary() );
00860 }
00861
00862 QString sLocation = i18n( "Location unspecified" );
00863 if ( ! event->location().isEmpty() ) {
00864 sLocation = QStyleSheet::escape( event->location() );
00865 }
00866
00867 QString dir = ( QApplication::reverseLayout() ? "rtl" : "ltr" );
00868 html = QString("<div dir=\"%1\">\n").arg(dir);
00869
00870 html += "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n";
00871
00872
00873 html += invitationRow( i18n( "What:" ), sSummary );
00874 html += invitationRow( i18n( "Where:" ), sLocation );
00875
00876
00877 if ( event->dtStart().date() == event->dtEnd().date() ) {
00878 html += invitationRow( i18n( "Date:" ), event->dtStartDateStr( false ) );
00879 if ( !event->doesFloat() ) {
00880 html += invitationRow( i18n( "Time:" ),
00881 event->dtStartTimeStr() + " - " + event->dtEndTimeStr() );
00882 }
00883 } else {
00884 html += invitationRow( i18n( "Starting date of an event", "From:" ), event->dtStartDateStr( false ) );
00885 if ( !event->doesFloat() ) {
00886 html += invitationRow( i18n( "Starting time of an event", "At:" ), event->dtStartTimeStr() );
00887 }
00888 if ( event->hasEndDate() ) {
00889 html += invitationRow( i18n( "Ending date of an event", "To:" ), event->dtEndDateStr( false ) );
00890 if ( !event->doesFloat() ) {
00891 html += invitationRow( i18n( "Starting time of an event", "At:" ), event->dtEndTimeStr() );
00892 }
00893 } else {
00894 html += invitationRow( i18n( "Ending date of an event", "To:" ), i18n( "no end date specified" ) );
00895 }
00896 }
00897
00898
00899 if ( !event->doesFloat() && event->hasEndDate() ) {
00900 tmp = QString::null;
00901 QTime sDuration(0,0,0), t;
00902 int secs = event->dtStart().secsTo( event->dtEnd() );
00903 t = sDuration.addSecs( secs );
00904 if ( t.hour() > 0 ) {
00905 tmp += i18n( "1 hour ", "%n hours ", t.hour() );
00906 }
00907 if ( t.minute() > 0 ) {
00908 tmp += i18n( "1 minute ", "%n minutes ", t.minute() );
00909 }
00910
00911 html += invitationRow( i18n( "Duration:" ), tmp );
00912 }
00913
00914 if ( event->doesRecur() )
00915 html += invitationRow( i18n( "Recurrence:" ), IncidenceFormatter::recurrenceString( event ) );
00916
00917 html += "</table>\n";
00918 html += invitationsDetailsIncidence( event );
00919 html += "</div>\n";
00920
00921 return html;
00922 }
00923
00924 static QString invitationDetailsTodo( Todo *todo )
00925 {
00926
00927 if ( !todo )
00928 return QString::null;
00929
00930 QString sSummary = i18n( "Summary unspecified" );
00931 if ( ! todo->summary().isEmpty() ) {
00932 sSummary = QStyleSheet::escape( todo->summary() );
00933 }
00934
00935 QString sLocation = i18n( "Location unspecified" );
00936 if ( ! todo->location().isEmpty() ) {
00937 sLocation = QStyleSheet::escape( todo->location() );
00938 }
00939
00940 QString dir = ( QApplication::reverseLayout() ? "rtl" : "ltr" );
00941 QString html = QString("<div dir=\"%1\">\n").arg(dir);
00942
00943 html += "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n";
00944
00945
00946 html += invitationRow( i18n( "What:" ), sSummary );
00947 html += invitationRow( i18n( "Where:" ), sLocation );
00948
00949 if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
00950 html += invitationRow( i18n( "Start Date:" ), todo->dtStartStr( false ) );
00951 }
00952 if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
00953 html += invitationRow( i18n( "Due Date:" ), todo->dtDueDateStr( false ) );
00954 if ( !todo->doesFloat() ) {
00955 html += invitationRow( i18n( "Due Time:" ),
00956 KGlobal::locale()->formatTime( todo->dtDue().time() ) );
00957 }
00958
00959 } else {
00960 html += invitationRow( i18n( "Due Date:" ), i18n( "Due Date: None", "None" ) );
00961 }
00962
00963 html += "</table></div>\n";
00964 html += invitationsDetailsIncidence( todo );
00965
00966 return html;
00967 }
00968
00969 static QString invitationDetailsJournal( Journal *journal )
00970 {
00971 if ( !journal )
00972 return QString::null;
00973
00974 QString sSummary = i18n( "Summary unspecified" );
00975 QString sDescr = i18n( "Description unspecified" );
00976 if ( ! journal->summary().isEmpty() ) {
00977 sSummary = journal->summary();
00978 }
00979 if ( ! journal->description().isEmpty() ) {
00980 sDescr = journal->description();
00981 }
00982 QString html( "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n" );
00983 html += invitationRow( i18n( "Summary:" ), sSummary );
00984 html += invitationRow( i18n( "Date:" ), journal->dtStartDateStr( false ) );
00985 html += invitationRow( i18n( "Description:" ), sDescr );
00986 html += "</table>\n";
00987 html += invitationsDetailsIncidence( journal );
00988
00989 return html;
00990 }
00991
00992 static QString invitationDetailsFreeBusy( FreeBusy *fb )
00993 {
00994 if ( !fb )
00995 return QString::null;
00996 QString html( "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n" );
00997
00998 html += invitationRow( i18n("Person:"), fb->organizer().fullName() );
00999 html += invitationRow( i18n("Start date:"), fb->dtStartDateStr() );
01000 html += invitationRow( i18n("End date:"),
01001 KGlobal::locale()->formatDate( fb->dtEnd().date(), true ) );
01002 html += "<tr><td colspan=2><hr></td></tr>\n";
01003 html += "<tr><td colspan=2>Busy periods given in this free/busy object:</td></tr>\n";
01004
01005 QValueList<Period> periods = fb->busyPeriods();
01006
01007 QValueList<Period>::iterator it;
01008 for ( it = periods.begin(); it != periods.end(); ++it ) {
01009 Period per = *it;
01010 if ( per.hasDuration() ) {
01011 int dur = per.duration().asSeconds();
01012 QString cont;
01013 if ( dur >= 3600 ) {
01014 cont += i18n("1 hour ", "%n hours ", dur / 3600);
01015 dur %= 3600;
01016 }
01017 if ( dur >= 60 ) {
01018 cont += i18n("1 minute", "%n minutes ", dur / 60);
01019 dur %= 60;
01020 }
01021 if ( dur > 0 ) {
01022 cont += i18n("1 second", "%n seconds", dur);
01023 }
01024 html += invitationRow( QString::null, i18n("startDate for duration", "%1 for %2")
01025 .arg( KGlobal::locale()->formatDateTime( per.start(), false ) )
01026 .arg(cont) );
01027 } else {
01028 QString cont;
01029 if ( per.start().date() == per.end().date() ) {
01030 cont = i18n("date, fromTime - toTime ", "%1, %2 - %3")
01031 .arg( KGlobal::locale()->formatDate( per.start().date() ) )
01032 .arg( KGlobal::locale()->formatTime( per.start().time() ) )
01033 .arg( KGlobal::locale()->formatTime( per.end().time() ) );
01034 } else {
01035 cont = i18n("fromDateTime - toDateTime", "%1 - %2")
01036 .arg( KGlobal::locale()->formatDateTime( per.start(), false ) )
01037 .arg( KGlobal::locale()->formatDateTime( per.end(), false ) );
01038 }
01039
01040 html += invitationRow( QString::null, cont );
01041 }
01042 }
01043
01044 html += "</table>\n";
01045 return html;
01046 }
01047
01048 static QString invitationHeaderEvent( Event *event, ScheduleMessage *msg )
01049 {
01050 if ( !msg || !event )
01051 return QString::null;
01052
01053 switch ( msg->method() ) {
01054 case Scheduler::Publish:
01055 return i18n( "This event has been published" );
01056 case Scheduler::Request:
01057 if ( event->revision() > 0 ) {
01058 return i18n( "This invitation has been updated" );
01059 }
01060 if ( iamOrganizer( event ) ) {
01061 return i18n( "I sent this invitation" );
01062 } else {
01063 if ( !event->organizer().fullName().isEmpty() ) {
01064 return i18n( "You received an invitation from %1" ).
01065 arg( event->organizer().fullName() );
01066 } else {
01067 return i18n( "You received an invitation" );
01068 }
01069 }
01070 case Scheduler::Refresh:
01071 return i18n( "This invitation was refreshed" );
01072 case Scheduler::Cancel:
01073 return i18n( "This invitation has been canceled" );
01074 case Scheduler::Add:
01075 return i18n( "Addition to the invitation" );
01076 case Scheduler::Reply: {
01077 Attendee::List attendees = event->attendees();
01078 if( attendees.count() == 0 ) {
01079 kdDebug(5850) << "No attendees in the iCal reply!" << endl;
01080 return QString::null;
01081 }
01082 if( attendees.count() != 1 ) {
01083 kdDebug(5850) << "Warning: attendeecount in the reply should be 1 "
01084 << "but is " << attendees.count() << endl;
01085 }
01086 Attendee* attendee = *attendees.begin();
01087 QString attendeeName = attendee->name();
01088 if ( attendeeName.isEmpty() ) {
01089 attendeeName = attendee->email();
01090 }
01091 if ( attendeeName.isEmpty() ) {
01092 attendeeName = i18n( "Sender" );
01093 }
01094
01095 QString delegatorName, dummy;
01096 KPIM::getNameAndMail( attendee->delegator(), delegatorName, dummy );
01097 if ( delegatorName.isEmpty() ) {
01098 delegatorName = attendee->delegator();
01099 }
01100
01101 switch( attendee->status() ) {
01102 case Attendee::NeedsAction:
01103 return i18n( "%1 indicates this invitation still needs some action" ).arg( attendeeName );
01104 case Attendee::Accepted:
01105 if ( delegatorName.isEmpty() ) {
01106 return i18n( "%1 accepts this invitation" ).arg( attendeeName );
01107 } else {
01108 return i18n( "%1 accepts this invitation on behalf of %2" ).
01109 arg( attendeeName ).arg( delegatorName );
01110 }
01111 case Attendee::Tentative:
01112 if ( delegatorName.isEmpty() ) {
01113 return i18n( "%1 tentatively accepts this invitation" ).
01114 arg( attendeeName );
01115 } else {
01116 return i18n( "%1 tentatively accepts this invitation on behalf of %2" ).
01117 arg( attendeeName ).arg( delegatorName );
01118 }
01119 case Attendee::Declined:
01120 if ( delegatorName.isEmpty() ) {
01121 return i18n( "%1 declines this invitation" ).arg( attendeeName );
01122 } else {
01123 return i18n( "%1 declines this invitation on behalf of %2" ).
01124 arg( attendeeName ).arg( delegatorName );
01125 }
01126 case Attendee::Delegated: {
01127 QString delegate, dummy;
01128 KPIM::getNameAndMail( attendee->delegate(), delegate, dummy );
01129 if ( delegate.isEmpty() ) {
01130 delegate = attendee->delegate();
01131 }
01132 if ( !delegate.isEmpty() ) {
01133 return i18n( "%1 has delegated this invitation to %2" ).
01134 arg( attendeeName ) .arg( delegate );
01135 } else {
01136 return i18n( "%1 has delegated this invitation" ).arg( attendeeName );
01137 }
01138 }
01139 case Attendee::Completed:
01140 return i18n( "This invitation is now completed" );
01141 case Attendee::InProcess:
01142 return i18n( "%1 is still processing the invitation" ).
01143 arg( attendeeName );
01144 default:
01145 return i18n( "Unknown response to this invitation" );
01146 }
01147 break; }
01148 case Scheduler::Counter:
01149 return i18n( "Sender makes this counter proposal" );
01150 case Scheduler::Declinecounter:
01151 return i18n( "Sender declines the counter proposal" );
01152 case Scheduler::NoMethod:
01153 return i18n("Error: iMIP message with unknown method: '%1'").
01154 arg( msg->method() );
01155 }
01156 return QString::null;
01157 }
01158
01159 static QString invitationHeaderTodo( Todo *todo, ScheduleMessage *msg )
01160 {
01161 if ( !msg || !todo ) {
01162 return QString::null;
01163 }
01164
01165 switch ( msg->method() ) {
01166 case Scheduler::Publish:
01167 return i18n("This task has been published");
01168 case Scheduler::Request:
01169 if ( todo->revision() > 0 ) {
01170 return i18n( "This task has been updated" );
01171 } else {
01172 return i18n( "You have been assigned this task" );
01173 }
01174 case Scheduler::Refresh:
01175 return i18n( "This task was refreshed" );
01176 case Scheduler::Cancel:
01177 return i18n( "This task was canceled" );
01178 case Scheduler::Add:
01179 return i18n( "Addition to the task" );
01180 case Scheduler::Reply: {
01181 Attendee::List attendees = todo->attendees();
01182 if( attendees.count() == 0 ) {
01183 kdDebug(5850) << "No attendees in the iCal reply!" << endl;
01184 return QString::null;
01185 }
01186 if( attendees.count() != 1 ) {
01187 kdDebug(5850) << "Warning: attendeecount in the reply should be 1 "
01188 << "but is " << attendees.count() << endl;
01189 }
01190 Attendee* attendee = *attendees.begin();
01191
01192 switch( attendee->status() ) {
01193 case Attendee::NeedsAction:
01194 return i18n( "Sender indicates this task assignment still needs some action" );
01195 case Attendee::Accepted:
01196 return i18n( "Sender accepts this task" );
01197 case Attendee::Tentative:
01198 return i18n( "Sender tentatively accepts this task" );
01199 case Attendee::Declined:
01200 return i18n( "Sender declines this task" );
01201 case Attendee::Delegated: {
01202 QString delegate, dummy;
01203 KPIM::getNameAndMail( attendee->delegate(), delegate, dummy );
01204 if ( delegate.isEmpty() ) {
01205 delegate = attendee->delegate();
01206 }
01207 if ( !delegate.isEmpty() ) {
01208 return i18n( "Sender has delegated this request for the task to %1" ).arg( delegate );
01209 } else {
01210 return i18n( "Sender has delegated this request for the task " );
01211 }
01212 }
01213 case Attendee::Completed:
01214 return i18n( "The request for this task is now completed" );
01215 case Attendee::InProcess:
01216 return i18n( "Sender is still processing the invitation" );
01217 default:
01218 return i18n( "Unknown response to this task" );
01219 }
01220 break; }
01221 case Scheduler::Counter:
01222 return i18n( "Sender makes this counter proposal" );
01223 case Scheduler::Declinecounter:
01224 return i18n( "Sender declines the counter proposal" );
01225 case Scheduler::NoMethod:
01226 return i18n("Error: iMIP message with unknown method: '%1'").
01227 arg( msg->method() );
01228 }
01229 return QString::null;
01230 }
01231
01232 static QString invitationHeaderJournal( Journal *journal, ScheduleMessage *msg )
01233 {
01234
01235 if ( !msg || !journal ) {
01236 return QString::null;
01237 }
01238
01239 switch ( msg->method() ) {
01240 case Scheduler::Publish:
01241 return i18n("This journal has been published");
01242 case Scheduler::Request:
01243 return i18n( "You have been assigned this journal" );
01244 case Scheduler::Refresh:
01245 return i18n( "This journal was refreshed" );
01246 case Scheduler::Cancel:
01247 return i18n( "This journal was canceled" );
01248 case Scheduler::Add:
01249 return i18n( "Addition to the journal" );
01250 case Scheduler::Reply: {
01251 Attendee::List attendees = journal->attendees();
01252 if( attendees.count() == 0 ) {
01253 kdDebug(5850) << "No attendees in the iCal reply!" << endl;
01254 return QString::null;
01255 }
01256 if( attendees.count() != 1 ) {
01257 kdDebug(5850) << "Warning: attendeecount in the reply should be 1 "
01258 << "but is " << attendees.count() << endl;
01259 }
01260 Attendee* attendee = *attendees.begin();
01261
01262 switch( attendee->status() ) {
01263 case Attendee::NeedsAction:
01264 return i18n( "Sender indicates this journal assignment still needs some action" );
01265 case Attendee::Accepted:
01266 return i18n( "Sender accepts this journal" );
01267 case Attendee::Tentative:
01268 return i18n( "Sender tentatively accepts this journal" );
01269 case Attendee::Declined:
01270 return i18n( "Sender declines this journal" );
01271 case Attendee::Delegated:
01272 return i18n( "Sender has delegated this request for the journal" );
01273 case Attendee::Completed:
01274 return i18n( "The request for this journal is now completed" );
01275 case Attendee::InProcess:
01276 return i18n( "Sender is still processing the invitation" );
01277 default:
01278 return i18n( "Unknown response to this journal" );
01279 }
01280 break;
01281 }
01282 case Scheduler::Counter:
01283 return i18n( "Sender makes this counter proposal" );
01284 case Scheduler::Declinecounter:
01285 return i18n( "Sender declines the counter proposal" );
01286 case Scheduler::NoMethod:
01287 return i18n("Error: iMIP message with unknown method: '%1'").
01288 arg( msg->method() );
01289 }
01290 return QString::null;
01291 }
01292
01293 static QString invitationHeaderFreeBusy( FreeBusy *fb, ScheduleMessage *msg )
01294 {
01295 if ( !msg || !fb ) {
01296 return QString::null;
01297 }
01298
01299 switch ( msg->method() ) {
01300 case Scheduler::Publish:
01301 return i18n("This free/busy list has been published");
01302 case Scheduler::Request:
01303 return i18n( "The free/busy list has been requested" );
01304 case Scheduler::Refresh:
01305 return i18n( "This free/busy list was refreshed" );
01306 case Scheduler::Cancel:
01307 return i18n( "This free/busy list was canceled" );
01308 case Scheduler::Add:
01309 return i18n( "Addition to the free/busy list" );
01310 case Scheduler::NoMethod:
01311 default:
01312 return i18n("Error: Free/Busy iMIP message with unknown method: '%1'").
01313 arg( msg->method() );
01314 }
01315 }
01316
01317 static QString invitationAttendees( Incidence *incidence )
01318 {
01319 QString tmpStr;
01320 if ( !incidence ) {
01321 return tmpStr;
01322 }
01323
01324 tmpStr += eventViewerAddTag( "u", i18n( "Attendee List" ) );
01325 tmpStr += "<br/>";
01326
01327 int count=0;
01328 Attendee::List attendees = incidence->attendees();
01329 if ( !attendees.isEmpty() ) {
01330
01331 Attendee::List::ConstIterator it;
01332 for( it = attendees.begin(); it != attendees.end(); ++it ) {
01333 Attendee *a = *it;
01334 if ( !iamAttendee( a ) ) {
01335 count++;
01336 if ( count == 1 ) {
01337 tmpStr += "<table border=\"1\" cellpadding=\"1\" cellspacing=\"0\" columns=\"2\">";
01338 }
01339 tmpStr += "<tr>";
01340 tmpStr += "<td>";
01341 tmpStr += invitationPerson( a->email(), a->name(), QString::null );
01342 if ( !a->delegator().isEmpty() ) {
01343 tmpStr += i18n(" (delegated by %1)" ).arg( a->delegator() );
01344 }
01345 if ( !a->delegate().isEmpty() ) {
01346 tmpStr += i18n(" (delegated to %1)" ).arg( a->delegate() );
01347 }
01348 tmpStr += "</td>";
01349 tmpStr += "<td>" + a->statusStr() + "</td>";
01350 tmpStr += "</tr>";
01351 }
01352 }
01353 }
01354 if ( count ) {
01355 tmpStr += "</table>";
01356 } else {
01357 tmpStr += "<i>" + i18n( "No attendee", "None" ) + "</i>";
01358 }
01359
01360 return tmpStr;
01361 }
01362
01363 static QString invitationAttachments( InvitationFormatterHelper *helper, Incidence *incidence )
01364 {
01365 QString tmpStr;
01366 if ( !incidence ) {
01367 return tmpStr;
01368 }
01369
01370 Attachment::List attachments = incidence->attachments();
01371 if ( !attachments.isEmpty() ) {
01372 tmpStr += i18n( "Attached Documents:" ) + "<ol>";
01373
01374 Attachment::List::ConstIterator it;
01375 for( it = attachments.begin(); it != attachments.end(); ++it ) {
01376 Attachment *a = *it;
01377 tmpStr += "<li>";
01378
01379 KMimeType::Ptr mimeType = KMimeType::mimeType( a->mimeType() );
01380 QString iconStr = mimeType->icon( a->uri(), false );
01381 QString iconPath = KGlobal::iconLoader()->iconPath( iconStr, KIcon::Small );
01382 if ( !iconPath.isEmpty() ) {
01383 tmpStr += "<img src=\"" + iconPath + "\" align=\"top\">";
01384 }
01385 tmpStr += helper->makeLink( "ATTACH:" + a->label(), a->label() );
01386 tmpStr += "</li>";
01387 }
01388 tmpStr += "</ol>";
01389 }
01390
01391 return tmpStr;
01392 }
01393
01394 class IncidenceFormatter::ScheduleMessageVisitor : public IncidenceBase::Visitor
01395 {
01396 public:
01397 ScheduleMessageVisitor() : mMessage(0) { mResult = ""; }
01398 bool act( IncidenceBase *incidence, ScheduleMessage *msg ) { mMessage = msg; return incidence->accept( *this ); }
01399 QString result() const { return mResult; }
01400
01401 protected:
01402 QString mResult;
01403 ScheduleMessage *mMessage;
01404 };
01405
01406 class IncidenceFormatter::InvitationHeaderVisitor :
01407 public IncidenceFormatter::ScheduleMessageVisitor
01408 {
01409 protected:
01410 bool visit( Event *event )
01411 {
01412 mResult = invitationHeaderEvent( event, mMessage );
01413 return !mResult.isEmpty();
01414 }
01415 bool visit( Todo *todo )
01416 {
01417 mResult = invitationHeaderTodo( todo, mMessage );
01418 return !mResult.isEmpty();
01419 }
01420 bool visit( Journal *journal )
01421 {
01422 mResult = invitationHeaderJournal( journal, mMessage );
01423 return !mResult.isEmpty();
01424 }
01425 bool visit( FreeBusy *fb )
01426 {
01427 mResult = invitationHeaderFreeBusy( fb, mMessage );
01428 return !mResult.isEmpty();
01429 }
01430 };
01431
01432 class IncidenceFormatter::InvitationBodyVisitor :
01433 public IncidenceFormatter::ScheduleMessageVisitor
01434 {
01435 protected:
01436 bool visit( Event *event )
01437 {
01438 mResult = invitationDetailsEvent( event );
01439 return !mResult.isEmpty();
01440 }
01441 bool visit( Todo *todo )
01442 {
01443 mResult = invitationDetailsTodo( todo );
01444 return !mResult.isEmpty();
01445 }
01446 bool visit( Journal *journal )
01447 {
01448 mResult = invitationDetailsJournal( journal );
01449 return !mResult.isEmpty();
01450 }
01451 bool visit( FreeBusy *fb )
01452 {
01453 mResult = invitationDetailsFreeBusy( fb );
01454 return !mResult.isEmpty();
01455 }
01456 };
01457
01458 class IncidenceFormatter::IncidenceCompareVisitor :
01459 public IncidenceBase::Visitor
01460 {
01461 public:
01462 IncidenceCompareVisitor() : mExistingIncidence(0) {}
01463 bool act( IncidenceBase *incidence, Incidence* existingIncidence )
01464 {
01465 Incidence *inc = dynamic_cast<Incidence*>( incidence );
01466 if ( !inc || !existingIncidence || inc->revision() <= existingIncidence->revision() )
01467 return false;
01468 mExistingIncidence = existingIncidence;
01469 return incidence->accept( *this );
01470 }
01471
01472 QString result() const
01473 {
01474 if ( mChanges.isEmpty() )
01475 return QString();
01476 QString html = "<div align=\"left\"><ul><li>";
01477 html += mChanges.join( "</li><li>" );
01478 html += "</li><ul></div>";
01479 return html;
01480 }
01481
01482 protected:
01483 bool visit( Event *event )
01484 {
01485 compareEvents( event, dynamic_cast<Event*>( mExistingIncidence ) );
01486 compareIncidences( event, mExistingIncidence );
01487 return !mChanges.isEmpty();
01488 }
01489 bool visit( Todo *todo )
01490 {
01491 compareIncidences( todo, mExistingIncidence );
01492 return !mChanges.isEmpty();
01493 }
01494 bool visit( Journal *journal )
01495 {
01496 compareIncidences( journal, mExistingIncidence );
01497 return !mChanges.isEmpty();
01498 }
01499 bool visit( FreeBusy *fb )
01500 {
01501 Q_UNUSED( fb );
01502 return !mChanges.isEmpty();
01503 }
01504
01505 private:
01506 void compareEvents( Event *newEvent, Event *oldEvent )
01507 {
01508 if ( !oldEvent || !newEvent )
01509 return;
01510 if ( oldEvent->dtStart() != newEvent->dtStart() || oldEvent->doesFloat() != newEvent->doesFloat() )
01511 mChanges += i18n( "The invitation starting time has been changed from %1 to %2" )
01512 .arg( eventStartTimeStr( oldEvent ) ).arg( eventStartTimeStr( newEvent ) );
01513 if ( oldEvent->dtEnd() != newEvent->dtEnd() || oldEvent->doesFloat() != newEvent->doesFloat() )
01514 mChanges += i18n( "The invitation ending time has been changed from %1 to %2" )
01515 .arg( eventEndTimeStr( oldEvent ) ).arg( eventEndTimeStr( newEvent ) );
01516 }
01517
01518 void compareIncidences( Incidence *newInc, Incidence *oldInc )
01519 {
01520 if ( !oldInc || !newInc )
01521 return;
01522 if ( oldInc->summary() != newInc->summary() )
01523 mChanges += i18n( "The summary has been changed to: \"%1\"" ).arg( newInc->summary() );
01524 if ( oldInc->location() != newInc->location() )
01525 mChanges += i18n( "The location has been changed to: \"%1\"" ).arg( newInc->location() );
01526 if ( oldInc->description() != newInc->description() )
01527 mChanges += i18n( "The description has been changed to: \"%1\"" ).arg( newInc->description() );
01528 Attendee::List oldAttendees = oldInc->attendees();
01529 Attendee::List newAttendees = newInc->attendees();
01530 for ( Attendee::List::ConstIterator it = newAttendees.constBegin(); it != newAttendees.constEnd(); ++it ) {
01531 Attendee *oldAtt = oldInc->attendeeByMail( (*it)->email() );
01532 if ( !oldAtt ) {
01533 mChanges += i18n( "Attendee %1 has been added" ).arg( (*it)->fullName() );
01534 } else {
01535 if ( oldAtt->status() != (*it)->status() )
01536 mChanges += i18n( "The status of attendee %1 has been changed to: %2" ).arg( (*it)->fullName() )
01537 .arg( (*it)->statusStr() );
01538 }
01539 }
01540 for ( Attendee::List::ConstIterator it = oldAttendees.constBegin(); it != oldAttendees.constEnd(); ++it ) {
01541 Attendee *newAtt = newInc->attendeeByMail( (*it)->email() );
01542 if ( !newAtt )
01543 mChanges += i18n( "Attendee %1 has been removed" ).arg( (*it)->fullName() );
01544 }
01545 }
01546
01547 private:
01548 Incidence* mExistingIncidence;
01549 QStringList mChanges;
01550 };
01551
01552
01553 QString InvitationFormatterHelper::makeLink( const QString &id, const QString &text )
01554 {
01555 if ( !id.startsWith( "ATTACH:" ) ) {
01556 QString res = QString( "<a href=\"%1\"><b>%2</b></a>" ).
01557 arg( generateLinkURL( id ), text );
01558 return res;
01559 } else {
01560
01561 QString res = QString( "<a href=\"%1\">%2</a>" ).
01562 arg( generateLinkURL( id ), text );
01563 return res;
01564 }
01565 }
01566
01567
01568
01569 static bool incidenceOwnedByMe( Calendar *calendar, Incidence *incidence )
01570 {
01571 CalendarResources *cal = dynamic_cast<CalendarResources*>( calendar );
01572 if ( !cal || !incidence ) {
01573 return true;
01574 }
01575 ResourceCalendar *res = cal->resource( incidence );
01576 if ( !res ) {
01577 return true;
01578 }
01579 const QString subRes = res->subresourceIdentifier( incidence );
01580 if ( !subRes.contains( "/.INBOX.directory/" ) ) {
01581 return false;
01582 }
01583 return true;
01584 }
01585
01586 QString IncidenceFormatter::formatICalInvitation( QString invitation, Calendar *mCalendar,
01587 InvitationFormatterHelper *helper )
01588 {
01589 if ( invitation.isEmpty() ) return QString::null;
01590
01591 ICalFormat format;
01592
01593 ScheduleMessage *msg = format.parseScheduleMessage( mCalendar, invitation );
01594
01595 if( !msg ) {
01596 kdDebug( 5850 ) << "Failed to parse the scheduling message" << endl;
01597 Q_ASSERT( format.exception() );
01598 kdDebug( 5850 ) << format.exception()->message() << endl;
01599 return QString::null;
01600 }
01601
01602 IncidenceBase *incBase = msg->event();
01603
01604
01605 Incidence *existingIncidence = 0;
01606 if ( incBase && helper->calendar() ) {
01607 existingIncidence = helper->calendar()->incidence( incBase->uid() );
01608 if ( !incidenceOwnedByMe( helper->calendar(), existingIncidence ) ) {
01609 existingIncidence = 0;
01610 }
01611 if ( !existingIncidence ) {
01612 const Incidence::List list = helper->calendar()->incidences();
01613 for ( Incidence::List::ConstIterator it = list.begin(), end = list.end(); it != end; ++it ) {
01614 if ( (*it)->schedulingID() == incBase->uid() &&
01615 incidenceOwnedByMe( helper->calendar(), *it ) ) {
01616 existingIncidence = *it;
01617 break;
01618 }
01619 }
01620 }
01621 }
01622
01623
01624 QString html;
01625
01626 QString tableStyle = QString::fromLatin1(
01627 "style=\"border: solid 1px; margin: 0em;\"" );
01628 QString tableHead = QString::fromLatin1(
01629 "<div align=\"center\">"
01630 "<table width=\"80%\" cellpadding=\"1\" cellspacing=\"0\" %1>"
01631 "<tr><td>").arg(tableStyle);
01632
01633 html += tableHead;
01634 InvitationHeaderVisitor headerVisitor;
01635
01636 if ( !headerVisitor.act( incBase, msg ) )
01637 return QString::null;
01638 html += "<b>" + headerVisitor.result() + "</b>";
01639
01640 InvitationBodyVisitor bodyVisitor;
01641 if ( !bodyVisitor.act( incBase, msg ) )
01642 return QString::null;
01643 html += bodyVisitor.result();
01644
01645 if ( msg->method() == Scheduler::Request ) {
01646 IncidenceCompareVisitor compareVisitor;
01647 if ( compareVisitor.act( incBase, existingIncidence ) ) {
01648 html += i18n("<p align=\"left\">The following changes have been made by the organizer:</p>");
01649 html += compareVisitor.result();
01650 }
01651 }
01652
01653 Incidence *inc = dynamic_cast<Incidence*>( incBase );
01654
01655
01656 bool myInc = iamOrganizer( inc );
01657
01658
01659 bool rsvpRec = false;
01660 Attendee *ea = 0;
01661 if ( !myInc ) {
01662 if ( existingIncidence ) {
01663 ea = findMyAttendee( existingIncidence );
01664 }
01665 if ( ea && ( ea->status() == Attendee::Accepted || ea->status() == Attendee::Declined ) ) {
01666 rsvpRec = true;
01667 }
01668 }
01669
01670
01671 bool rsvpReq = rsvpRequested( inc );
01672 if ( !myInc ) {
01673 html += "<br/>";
01674 html += "<i><u>";
01675 if ( rsvpRec && ( inc && inc->revision() == 0 ) ) {
01676 html += i18n( "Your response has already been recorded [%1]" ).
01677 arg( ea->statusStr() );
01678 rsvpReq = false;
01679 } else if ( msg->method() == Scheduler::Cancel ) {
01680 html += i18n( "This invitation was declined" );
01681 } else if ( msg->method() == Scheduler::Add ) {
01682 html += i18n( "This invitation was accepted" );
01683 } else {
01684 html += rsvpRequestedStr( rsvpReq );
01685 }
01686 html += "</u></i><br>";
01687 }
01688
01689
01690
01691 html += "<table border=\"0\" cellspacing=\"0\"><tr><td> </td></tr><tr>";
01692
01693 switch ( msg->method() ) {
01694 case Scheduler::Publish:
01695 case Scheduler::Request:
01696 case Scheduler::Refresh:
01697 case Scheduler::Add:
01698 {
01699 if ( inc && inc->revision() > 0 && ( existingIncidence || !helper->calendar() ) ) {
01700 if ( inc->type() == "Todo" ) {
01701 html += "<td colspan=\"9\">";
01702 html += helper->makeLink( "reply", i18n( "[Record invitation to my task list]" ) );
01703 } else {
01704 html += "<td colspan=\"13\">";
01705 html += helper->makeLink( "reply", i18n( "[Record invitation to my calendar]" ) );
01706 }
01707 html += "</td></tr><tr>";
01708 }
01709 html += "<td>";
01710
01711 if ( !myInc ) {
01712 if ( rsvpReq ) {
01713
01714 html += helper->makeLink( "accept", i18n( "[Accept]" ) );
01715 html += "</td><td> </td><td>";
01716 html += helper->makeLink( "accept_conditionally",
01717 i18n( "Accept conditionally", "[Accept cond.]" ) );
01718 html += "</td><td> </td><td>";
01719 }
01720
01721 if ( rsvpReq ) {
01722
01723 html += helper->makeLink( "counter", i18n( "[Counter proposal]" ) );
01724 html += "</td><td> </td><td>";
01725 }
01726
01727 if ( rsvpReq ) {
01728
01729 html += helper->makeLink( "decline", i18n( "[Decline]" ) );
01730 html += "</td><td> </td><td>";
01731 }
01732
01733 if ( !rsvpRec || ( inc && inc->revision() > 0 ) ) {
01734
01735 html += helper->makeLink( "delegate", i18n( "[Delegate]" ) );
01736 html += "</td><td> </td><td>";
01737
01738
01739 html += helper->makeLink( "forward", i18n( "[Forward]" ) );
01740
01741
01742 if ( inc && inc->type() == "Event" ) {
01743 html += "</td><td> </td><td>";
01744 html += helper->makeLink( "check_calendar", i18n("[Check my calendar]" ) );
01745 }
01746 }
01747 }
01748 break;
01749 }
01750
01751 case Scheduler::Cancel:
01752
01753 if ( inc->type() == "Todo" ) {
01754 html += helper->makeLink( "cancel", i18n( "[Remove invitation from my task list]" ) );
01755 } else {
01756 html += helper->makeLink( "cancel", i18n( "[Remove invitation from my calendar]" ) );
01757 }
01758 break;
01759
01760 case Scheduler::Reply:
01761 {
01762
01763 Attendee *a = 0;
01764 Attendee *ea = 0;
01765 if ( inc ) {
01766 a = inc->attendees().first();
01767 if ( a ) {
01768 ea = findAttendee( existingIncidence, a->email() );
01769 }
01770 }
01771 if ( ea && ( ea->status() != Attendee::NeedsAction ) && ( ea->status() == a->status() ) ) {
01772 if ( inc && inc->revision() > 0 ) {
01773 html += "<br><u><i>";
01774 html += i18n( "The response has been recorded [%1]" ).arg( ea->statusStr() );
01775 html += "</i></u>";
01776 }
01777 } else {
01778 if ( inc ) {
01779 if ( inc->type() == "Todo" ) {
01780 html += helper->makeLink( "reply", i18n( "[Record response into my task list]" ) );
01781 } else {
01782 html += helper->makeLink( "reply", i18n( "[Record response into my calendar]" ) );
01783 }
01784 }
01785 }
01786 break;
01787 }
01788
01789 case Scheduler::Counter:
01790
01791 html += helper->makeLink( "accept_counter", i18n("[Accept]") );
01792 html += " ";
01793 html += helper->makeLink( "decline_counter", i18n("[Decline]") );
01794 html += " ";
01795 html += helper->makeLink( "check_calendar", i18n("[Check my calendar]" ) );
01796 break;
01797
01798 case Scheduler::Declinecounter:
01799 case Scheduler::NoMethod:
01800 break;
01801 }
01802
01803
01804 html += "</td></tr></table>";
01805
01806
01807 if ( myInc && helper->calendar() ) {
01808 html += invitationAttendees( helper->calendar()->incidence( inc->uid() ) );
01809 }
01810
01811
01812 html += "</td></tr></table><br></div>";
01813
01814
01815 html += invitationAttachments( helper, inc );
01816
01817 return html;
01818 }
01819
01820
01821
01822
01823
01824
01825
01826
01827
01828
01829
01830 static QString stringProp( KTNEFMessage* tnefMsg, const Q_UINT32& key,
01831 const QString& fallback = QString::null)
01832 {
01833 return tnefMsg->findProp( key < 0x10000 ? key & 0xFFFF : key >> 16,
01834 fallback );
01835 }
01836
01837 static QString sNamedProp( KTNEFMessage* tnefMsg, const QString& name,
01838 const QString& fallback = QString::null )
01839 {
01840 return tnefMsg->findNamedProp( name, fallback );
01841 }
01842
01843 struct save_tz { char* old_tz; char* tz_env_str; };
01844
01845
01846 static struct save_tz set_tz( const char* _tc )
01847 {
01848 const char *tc = _tc?_tc:"UTC";
01849
01850 struct save_tz rv;
01851
01852 rv.old_tz = 0;
01853 rv.tz_env_str = 0;
01854
01855
01856
01857 char* tz_env = 0;
01858 if( getenv( "TZ" ) ) {
01859 tz_env = strdup( getenv( "TZ" ) );
01860 rv.old_tz = tz_env;
01861 }
01862 char* tmp_env = (char*)malloc( strlen( tc ) + 4 );
01863 strcpy( tmp_env, "TZ=" );
01864 strcpy( tmp_env+3, tc );
01865 putenv( tmp_env );
01866
01867 rv.tz_env_str = tmp_env;
01868
01869
01870
01871 tzset();
01872
01873
01874 return rv;
01875 }
01876
01877
01878 static void unset_tz( struct save_tz old_tz )
01879 {
01880 if( old_tz.old_tz ) {
01881 char* tmp_env = (char*)malloc( strlen( old_tz.old_tz ) + 4 );
01882 strcpy( tmp_env, "TZ=" );
01883 strcpy( tmp_env+3, old_tz.old_tz );
01884 putenv( tmp_env );
01885
01886 free( old_tz.old_tz );
01887 } else {
01888
01889 putenv( strdup("TZ") );
01890 }
01891 tzset();
01892
01893
01894 if( old_tz.tz_env_str ) free( old_tz.tz_env_str );
01895 }
01896
01897 static QDateTime utc2Local( const QDateTime& utcdt )
01898 {
01899 struct tm tmL;
01900
01901 save_tz tmp_tz = set_tz("UTC");
01902 time_t utc = utcdt.toTime_t();
01903 unset_tz( tmp_tz );
01904
01905 localtime_r( &utc, &tmL );
01906 return QDateTime( QDate( tmL.tm_year+1900, tmL.tm_mon+1, tmL.tm_mday ),
01907 QTime( tmL.tm_hour, tmL.tm_min, tmL.tm_sec ) );
01908 }
01909
01910
01911 static QDateTime pureISOToLocalQDateTime( const QString& dtStr,
01912 bool bDateOnly = false )
01913 {
01914 QDate tmpDate;
01915 QTime tmpTime;
01916 int year, month, day, hour, minute, second;
01917
01918 if( bDateOnly ) {
01919 year = dtStr.left( 4 ).toInt();
01920 month = dtStr.mid( 4, 2 ).toInt();
01921 day = dtStr.mid( 6, 2 ).toInt();
01922 hour = 0;
01923 minute = 0;
01924 second = 0;
01925 } else {
01926 year = dtStr.left( 4 ).toInt();
01927 month = dtStr.mid( 4, 2 ).toInt();
01928 day = dtStr.mid( 6, 2 ).toInt();
01929 hour = dtStr.mid( 9, 2 ).toInt();
01930 minute = dtStr.mid( 11, 2 ).toInt();
01931 second = dtStr.mid( 13, 2 ).toInt();
01932 }
01933 tmpDate.setYMD( year, month, day );
01934 tmpTime.setHMS( hour, minute, second );
01935
01936 if( tmpDate.isValid() && tmpTime.isValid() ) {
01937 QDateTime dT = QDateTime( tmpDate, tmpTime );
01938
01939 if( !bDateOnly ) {
01940
01941 if (dtStr.at(dtStr.length()-1) == 'Z') {
01942
01943
01944 dT = utc2Local( dT );
01945 }
01946 }
01947 return dT;
01948 } else
01949 return QDateTime();
01950 }
01951
01952
01953
01954 QString IncidenceFormatter::msTNEFToVPart( const QByteArray& tnef )
01955 {
01956 bool bOk = false;
01957
01958 KTNEFParser parser;
01959 QBuffer buf( tnef );
01960 CalendarLocal cal ( QString::fromLatin1( "UTC" ) );
01961 KABC::Addressee addressee;
01962 KABC::VCardConverter cardConv;
01963 ICalFormat calFormat;
01964 Event* event = new Event();
01965
01966 if( parser.openDevice( &buf ) ) {
01967 KTNEFMessage* tnefMsg = parser.message();
01968
01969
01970
01971
01972 QString msgClass = tnefMsg->findProp( 0x001A, QString::null, true )
01973 .upper();
01974 if( !msgClass.isEmpty() ) {
01975
01976
01977 bool bCompatClassAppointment = false;
01978 bool bCompatMethodRequest = false;
01979 bool bCompatMethodCancled = false;
01980 bool bCompatMethodAccepted = false;
01981 bool bCompatMethodAcceptedCond = false;
01982 bool bCompatMethodDeclined = false;
01983 if( msgClass.startsWith( "IPM.MICROSOFT SCHEDULE." ) ) {
01984 bCompatClassAppointment = true;
01985 if( msgClass.endsWith( ".MTGREQ" ) )
01986 bCompatMethodRequest = true;
01987 if( msgClass.endsWith( ".MTGCNCL" ) )
01988 bCompatMethodCancled = true;
01989 if( msgClass.endsWith( ".MTGRESPP" ) )
01990 bCompatMethodAccepted = true;
01991 if( msgClass.endsWith( ".MTGRESPA" ) )
01992 bCompatMethodAcceptedCond = true;
01993 if( msgClass.endsWith( ".MTGRESPN" ) )
01994 bCompatMethodDeclined = true;
01995 }
01996 bool bCompatClassNote = ( msgClass == "IPM.MICROSOFT MAIL.NOTE" );
01997
01998 if( bCompatClassAppointment || "IPM.APPOINTMENT" == msgClass ) {
01999
02000 bool bIsReply = false;
02001 QString prodID = "-//Microsoft Corporation//Outlook ";
02002 prodID += tnefMsg->findNamedProp( "0x8554", "9.0" );
02003 prodID += "MIMEDIR/EN\n";
02004 prodID += "VERSION:2.0\n";
02005 calFormat.setApplication( "Outlook", prodID );
02006
02007 Scheduler::Method method;
02008 if( bCompatMethodRequest )
02009 method = Scheduler::Request;
02010 else if( bCompatMethodCancled )
02011 method = Scheduler::Cancel;
02012 else if( bCompatMethodAccepted || bCompatMethodAcceptedCond ||
02013 bCompatMethodDeclined ) {
02014 method = Scheduler::Reply;
02015 bIsReply = true;
02016 } else {
02017
02018
02019
02020
02021
02022
02023
02024
02025 if( tnefMsg->findProp(0x0c17) == "1" )
02026 bIsReply = true;
02027 method = Scheduler::Request;
02028 }
02029
02031 ScheduleMessage schedMsg(event, method, ScheduleMessage::Unknown );
02032
02033 QString sSenderSearchKeyEmail( tnefMsg->findProp( 0x0C1D ) );
02034
02035 if( !sSenderSearchKeyEmail.isEmpty() ) {
02036 int colon = sSenderSearchKeyEmail.find( ':' );
02037
02038 if( sSenderSearchKeyEmail.find( ':' ) == -1 )
02039 sSenderSearchKeyEmail.remove( 0, colon+1 );
02040 }
02041
02042 QString s( tnefMsg->findProp( 0x0e04 ) );
02043 QStringList attendees = QStringList::split( ';', s );
02044 if( attendees.count() ) {
02045 for( QStringList::Iterator it = attendees.begin();
02046 it != attendees.end(); ++it ) {
02047
02048
02049 if( (*it).find('@') == -1 ) {
02050 s = (*it).stripWhiteSpace();
02051
02052 Attendee *attendee = new Attendee( s, s, true );
02053 if( bIsReply ) {
02054 if( bCompatMethodAccepted )
02055 attendee->setStatus( Attendee::Accepted );
02056 if( bCompatMethodDeclined )
02057 attendee->setStatus( Attendee::Declined );
02058 if( bCompatMethodAcceptedCond )
02059 attendee->setStatus(Attendee::Tentative);
02060 } else {
02061 attendee->setStatus( Attendee::NeedsAction );
02062 attendee->setRole( Attendee::ReqParticipant );
02063 }
02064 event->addAttendee(attendee);
02065 }
02066 }
02067 } else {
02068
02069
02070 s = sSenderSearchKeyEmail;
02071 if( !s.isEmpty() ) {
02072 Attendee *attendee = new Attendee( QString::null, QString::null,
02073 true );
02074 if( bIsReply ) {
02075 if( bCompatMethodAccepted )
02076 attendee->setStatus( Attendee::Accepted );
02077 if( bCompatMethodAcceptedCond )
02078 attendee->setStatus( Attendee::Declined );
02079 if( bCompatMethodDeclined )
02080 attendee->setStatus( Attendee::Tentative );
02081 } else {
02082 attendee->setStatus(Attendee::NeedsAction);
02083 attendee->setRole(Attendee::ReqParticipant);
02084 }
02085 event->addAttendee(attendee);
02086 }
02087 }
02088 s = tnefMsg->findProp( 0x0c1f );
02089 if( s.isEmpty() && !bIsReply )
02090 s = sSenderSearchKeyEmail;
02091
02092 if( !s.isEmpty() )
02093 event->setOrganizer( s );
02094
02095 s = tnefMsg->findProp( 0x8516 ).replace( QChar( '-' ), QString::null )
02096 .replace( QChar( ':' ), QString::null );
02097 event->setDtStart( QDateTime::fromString( s ) );
02098
02099 s = tnefMsg->findProp( 0x8517 ).replace( QChar( '-' ), QString::null )
02100 .replace( QChar( ':' ), QString::null );
02101 event->setDtEnd( QDateTime::fromString( s ) );
02102
02103 s = tnefMsg->findProp( 0x8208 );
02104 event->setLocation( s );
02105
02106
02107
02108
02109
02110
02111 s = tnefMsg->findProp( 0x0023 );
02112 event->setUid( s );
02113
02114
02115
02116
02117 s = tnefMsg->findProp( 0x8202 ).replace( QChar( '-' ), QString::null )
02118 .replace( QChar( ':' ), QString::null );
02119
02120
02121
02122 s = tnefMsg->findNamedProp( "Keywords" );
02123 event->setCategories( s );
02124
02125 s = tnefMsg->findProp( 0x1000 );
02126 event->setDescription( s );
02127
02128 s = tnefMsg->findProp( 0x0070 );
02129 event->setSummary( s );
02130
02131 s = tnefMsg->findProp( 0x0026 );
02132 event->setPriority( s.toInt() );
02133
02134
02135 if(!tnefMsg->findProp(0x8503).isEmpty()) {
02136 Alarm *alarm = new Alarm(event);
02137 QDateTime highNoonTime =
02138 pureISOToLocalQDateTime( tnefMsg->findProp( 0x8502 )
02139 .replace( QChar( '-' ), "" )
02140 .replace( QChar( ':' ), "" ) );
02141 QDateTime wakeMeUpTime =
02142 pureISOToLocalQDateTime( tnefMsg->findProp( 0x8560, "" )
02143 .replace( QChar( '-' ), "" )
02144 .replace( QChar( ':' ), "" ) );
02145 alarm->setTime(wakeMeUpTime);
02146
02147 if( highNoonTime.isValid() && wakeMeUpTime.isValid() )
02148 alarm->setStartOffset( Duration( highNoonTime, wakeMeUpTime ) );
02149 else
02150
02151 alarm->setStartOffset( Duration( 15*60 ) );
02152 alarm->setDisplayAlarm( i18n( "Reminder" ) );
02153
02154
02155
02156 event->addAlarm( alarm );
02157 }
02158 cal.addEvent( event );
02159 bOk = true;
02160
02161 } else if( bCompatClassNote || "IPM.CONTACT" == msgClass ) {
02162 addressee.setUid( stringProp( tnefMsg, attMSGID ) );
02163 addressee.setFormattedName( stringProp( tnefMsg, MAPI_TAG_PR_DISPLAY_NAME ) );
02164 addressee.insertEmail( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_EMAIL1EMAILADDRESS ), true );
02165 addressee.insertEmail( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_EMAIL2EMAILADDRESS ), false );
02166 addressee.insertEmail( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_EMAIL3EMAILADDRESS ), false );
02167 addressee.insertCustom( "KADDRESSBOOK", "X-IMAddress", sNamedProp( tnefMsg, MAPI_TAG_CONTACT_IMADDRESS ) );
02168 addressee.insertCustom( "KADDRESSBOOK", "X-SpousesName", stringProp( tnefMsg, MAPI_TAG_PR_SPOUSE_NAME ) );
02169 addressee.insertCustom( "KADDRESSBOOK", "X-ManagersName", stringProp( tnefMsg, MAPI_TAG_PR_MANAGER_NAME ) );
02170 addressee.insertCustom( "KADDRESSBOOK", "X-AssistantsName", stringProp( tnefMsg, MAPI_TAG_PR_ASSISTANT ) );
02171 addressee.insertCustom( "KADDRESSBOOK", "X-Department", stringProp( tnefMsg, MAPI_TAG_PR_DEPARTMENT_NAME ) );
02172 addressee.insertCustom( "KADDRESSBOOK", "X-Office", stringProp( tnefMsg, MAPI_TAG_PR_OFFICE_LOCATION ) );
02173 addressee.insertCustom( "KADDRESSBOOK", "X-Profession", stringProp( tnefMsg, MAPI_TAG_PR_PROFESSION ) );
02174
02175 QString s = tnefMsg->findProp( MAPI_TAG_PR_WEDDING_ANNIVERSARY )
02176 .replace( QChar( '-' ), QString::null )
02177 .replace( QChar( ':' ), QString::null );
02178 if( !s.isEmpty() )
02179 addressee.insertCustom( "KADDRESSBOOK", "X-Anniversary", s );
02180
02181 addressee.setUrl( KURL( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_WEBPAGE ) ) );
02182
02183
02184 addressee.setFamilyName( stringProp( tnefMsg, MAPI_TAG_PR_SURNAME ) );
02185 addressee.setGivenName( stringProp( tnefMsg, MAPI_TAG_PR_GIVEN_NAME ) );
02186 addressee.setAdditionalName( stringProp( tnefMsg, MAPI_TAG_PR_MIDDLE_NAME ) );
02187 addressee.setPrefix( stringProp( tnefMsg, MAPI_TAG_PR_DISPLAY_NAME_PREFIX ) );
02188 addressee.setSuffix( stringProp( tnefMsg, MAPI_TAG_PR_GENERATION ) );
02189
02190 addressee.setNickName( stringProp( tnefMsg, MAPI_TAG_PR_NICKNAME ) );
02191 addressee.setRole( stringProp( tnefMsg, MAPI_TAG_PR_TITLE ) );
02192 addressee.setOrganization( stringProp( tnefMsg, MAPI_TAG_PR_COMPANY_NAME ) );
02193
02194
02195
02196
02197
02198 KABC::Address adr;
02199 adr.setPostOfficeBox( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_PO_BOX ) );
02200 adr.setStreet( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_STREET ) );
02201 adr.setLocality( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_CITY ) );
02202 adr.setRegion( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_STATE_OR_PROVINCE ) );
02203 adr.setPostalCode( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_POSTAL_CODE ) );
02204 adr.setCountry( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_COUNTRY ) );
02205 adr.setType(KABC::Address::Home);
02206 addressee.insertAddress(adr);
02207
02208 adr.setPostOfficeBox( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSPOBOX ) );
02209 adr.setStreet( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSSTREET ) );
02210 adr.setLocality( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSCITY ) );
02211 adr.setRegion( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSSTATE ) );
02212 adr.setPostalCode( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSPOSTALCODE ) );
02213 adr.setCountry( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSCOUNTRY ) );
02214 adr.setType( KABC::Address::Work );
02215 addressee.insertAddress( adr );
02216
02217 adr.setPostOfficeBox( stringProp( tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_PO_BOX ) );
02218 adr.setStreet( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_STREET ) );
02219 adr.setLocality( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_CITY ) );
02220 adr.setRegion( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_STATE_OR_PROVINCE ) );
02221 adr.setPostalCode( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_POSTAL_CODE ) );
02222 adr.setCountry( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_COUNTRY ) );
02223 adr.setType( KABC::Address::Dom );
02224 addressee.insertAddress(adr);
02225
02226
02227
02228
02229
02230 QString nr;
02231 nr = stringProp( tnefMsg, MAPI_TAG_PR_HOME_TELEPHONE_NUMBER );
02232 addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Home ) );
02233 nr = stringProp( tnefMsg, MAPI_TAG_PR_BUSINESS_TELEPHONE_NUMBER );
02234 addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Work ) );
02235 nr = stringProp( tnefMsg, MAPI_TAG_PR_MOBILE_TELEPHONE_NUMBER );
02236 addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Cell ) );
02237 nr = stringProp( tnefMsg, MAPI_TAG_PR_HOME_FAX_NUMBER );
02238 addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Fax | KABC::PhoneNumber::Home ) );
02239 nr = stringProp( tnefMsg, MAPI_TAG_PR_BUSINESS_FAX_NUMBER );
02240 addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Fax | KABC::PhoneNumber::Work ) );
02241
02242 s = tnefMsg->findProp( MAPI_TAG_PR_BIRTHDAY )
02243 .replace( QChar( '-' ), QString::null )
02244 .replace( QChar( ':' ), QString::null );
02245 if( !s.isEmpty() )
02246 addressee.setBirthday( QDateTime::fromString( s ) );
02247
02248 bOk = ( !addressee.isEmpty() );
02249 } else if( "IPM.NOTE" == msgClass ) {
02250
02251 }
02252 }
02253 }
02254
02255
02256 QString iCal = calFormat.toString( &cal );
02257 if( !iCal.isEmpty() )
02258
02259 return iCal;
02260
02261
02262 KABC::VCardConverter converter;
02263 return converter.createVCard( addressee );
02264 }
02265
02266
02267 QString IncidenceFormatter::formatTNEFInvitation( const QByteArray& tnef,
02268 Calendar *mCalendar, InvitationFormatterHelper *helper )
02269 {
02270 QString vPart = IncidenceFormatter::msTNEFToVPart( tnef );
02271 QString iCal = IncidenceFormatter::formatICalInvitation( vPart, mCalendar, helper );
02272 if( !iCal.isEmpty() )
02273 return iCal;
02274 return vPart;
02275 }
02276
02277
02278
02279
02280
02281
02282
02283
02284 class IncidenceFormatter::ToolTipVisitor : public IncidenceBase::Visitor
02285 {
02286 public:
02287 ToolTipVisitor() : mRichText( true ), mResult( "" ) {}
02288
02289 bool act( IncidenceBase *incidence, bool richText=true)
02290 {
02291 mRichText = richText;
02292 mResult = "";
02293 return incidence ? incidence->accept( *this ) : false;
02294 }
02295 QString result() const { return mResult; }
02296
02297 protected:
02298 bool visit( Event *event );
02299 bool visit( Todo *todo );
02300 bool visit( Journal *journal );
02301 bool visit( FreeBusy *fb );
02302
02303 QString dateRangeText( Event*event );
02304 QString dateRangeText( Todo *todo );
02305 QString dateRangeText( Journal *journal );
02306 QString dateRangeText( FreeBusy *fb );
02307
02308 QString generateToolTip( Incidence* incidence, QString dtRangeText );
02309
02310 protected:
02311 bool mRichText;
02312 QString mResult;
02313 };
02314
02315 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Event*event )
02316 {
02317 QString ret;
02318 QString tmp;
02319 if ( event->isMultiDay() ) {
02320
02321 tmp = "<br>" + i18n("Event start", "<i>From:</i> %1");
02322 if (event->doesFloat())
02323 ret += tmp.arg( event->dtStartDateStr().replace(" ", " ") );
02324 else
02325 ret += tmp.arg( event->dtStartStr().replace(" ", " ") );
02326
02327 tmp = "<br>" + i18n("Event end","<i>To:</i> %1");
02328 if (event->doesFloat())
02329 ret += tmp.arg( event->dtEndDateStr().replace(" ", " ") );
02330 else
02331 ret += tmp.arg( event->dtEndStr().replace(" ", " ") );
02332
02333 } else {
02334
02335 ret += "<br>"+i18n("<i>Date:</i> %1").
02336 arg( event->dtStartDateStr().replace(" ", " ") );
02337 if ( !event->doesFloat() ) {
02338 const QString dtStartTime = event->dtStartTimeStr().replace( " ", " " );
02339 const QString dtEndTime = event->dtEndTimeStr().replace( " ", " " );
02340 if ( dtStartTime == dtEndTime ) {
02341 tmp = "<br>" + i18n("time for event, to prevent ugly line breaks",
02342 "<i>Time:</i> %1").
02343 arg( dtStartTime );
02344 } else {
02345 tmp = "<br>" + i18n("time range for event, to prevent ugly line breaks",
02346 "<i>Time:</i> %1 - %2").
02347 arg( dtStartTime, dtEndTime );
02348 }
02349 ret += tmp;
02350 }
02351
02352 }
02353 return ret;
02354 }
02355
02356 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Todo*todo )
02357 {
02358 QString ret;
02359 bool floats( todo->doesFloat() );
02360 if (todo->hasStartDate())
02361
02362
02363
02364 ret += "<br>" + i18n("<i>Start:</i> %1").arg(
02365 (floats)
02366 ?(todo->dtStartDateStr().replace(" ", " "))
02367 :(todo->dtStartStr().replace(" ", " ")) ) ;
02368 if (todo->hasDueDate())
02369 ret += "<br>" + i18n("<i>Due:</i> %1").arg(
02370 (floats)
02371 ?(todo->dtDueDateStr().replace(" ", " "))
02372 :(todo->dtDueStr().replace(" ", " ")) );
02373 if (todo->isCompleted())
02374 ret += "<br>" + i18n("<i>Completed:</i> %1").arg( todo->completedStr().replace(" ", " ") );
02375 else
02376 ret += "<br>" + i18n("%1 % completed").arg(todo->percentComplete());
02377
02378 return ret;
02379 }
02380
02381 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Journal*journal )
02382 {
02383 QString ret;
02384 if (journal->dtStart().isValid() ) {
02385 ret += "<br>" + i18n("<i>Date:</i> %1").arg( journal->dtStartDateStr( false ) );
02386 }
02387 return ret;
02388 }
02389
02390 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( FreeBusy *fb )
02391 {
02392 QString tmp( "<br>" + i18n("<i>Period start:</i> %1") );
02393 QString ret = tmp.arg( KGlobal::locale()->formatDateTime( fb->dtStart() ) );
02394 tmp = "<br>" + i18n("<i>Period start:</i> %1");
02395 ret += tmp.arg( KGlobal::locale()->formatDateTime( fb->dtEnd() ) );
02396 return ret;
02397 }
02398
02399
02400
02401 bool IncidenceFormatter::ToolTipVisitor::visit( Event *event )
02402 {
02403 mResult = generateToolTip( event, dateRangeText( event ) );
02404 return !mResult.isEmpty();
02405 }
02406
02407 bool IncidenceFormatter::ToolTipVisitor::visit( Todo *todo )
02408 {
02409 mResult = generateToolTip( todo, dateRangeText( todo ) );
02410 return !mResult.isEmpty();
02411 }
02412
02413 bool IncidenceFormatter::ToolTipVisitor::visit( Journal *journal )
02414 {
02415 mResult = generateToolTip( journal, dateRangeText( journal ) );
02416 return !mResult.isEmpty();
02417 }
02418
02419 bool IncidenceFormatter::ToolTipVisitor::visit( FreeBusy *fb )
02420 {
02421 mResult = "<qt><b>" + i18n("Free/Busy information for %1")
02422 .arg(fb->organizer().fullName()) + "</b>";
02423 mResult += dateRangeText( fb );
02424 mResult += "</qt>";
02425 return !mResult.isEmpty();
02426 }
02427
02428 QString IncidenceFormatter::ToolTipVisitor::generateToolTip( Incidence* incidence, QString dtRangeText )
02429 {
02430 if ( !incidence )
02431 return QString::null;
02432
02433 QString tmp = "<qt><b>"+ incidence->summary().replace("\n", "<br>")+"</b>";
02434
02435 tmp += dtRangeText;
02436
02437 if (!incidence->location().isEmpty()) {
02438
02439 tmp += "<br>"+i18n("<i>Location:</i> %1").
02440 arg( incidence->location().replace("\n", "<br>") );
02441 }
02442 if (!incidence->description().isEmpty()) {
02443 QString desc(incidence->description());
02444 if (desc.length()>120) {
02445 desc = desc.left(120) + "...";
02446 }
02447 tmp += "<br>----------<br>" + i18n("<i>Description:</i><br>") + desc.replace("\n", "<br>");
02448 }
02449 tmp += "</qt>";
02450 return tmp;
02451 }
02452
02453 QString IncidenceFormatter::toolTipString( IncidenceBase *incidence, bool richText )
02454 {
02455 ToolTipVisitor v;
02456 if ( v.act( incidence, richText ) ) {
02457 return v.result();
02458 } else
02459 return QString::null;
02460 }
02461
02462
02463
02464
02465
02466
02467
02468
02469 class IncidenceFormatter::MailBodyVisitor : public IncidenceBase::Visitor
02470 {
02471 public:
02472 MailBodyVisitor() : mResult( "" ) {}
02473
02474 bool act( IncidenceBase *incidence )
02475 {
02476 mResult = "";
02477 return incidence ? incidence->accept( *this ) : false;
02478 }
02479 QString result() const { return mResult; }
02480
02481 protected:
02482 bool visit( Event *event );
02483 bool visit( Todo *todo );
02484 bool visit( Journal *journal );
02485 bool visit( FreeBusy * ) { mResult = i18n("This is a Free Busy Object"); return !mResult.isEmpty(); }
02486 protected:
02487 QString mResult;
02488 };
02489
02490
02491 static QString mailBodyIncidence( Incidence *incidence )
02492 {
02493 QString body;
02494 if ( !incidence->summary().isEmpty() ) {
02495 body += i18n("Summary: %1\n").arg( incidence->summary() );
02496 }
02497 if ( !incidence->organizer().isEmpty() ) {
02498 body += i18n("Organizer: %1\n").arg( incidence->organizer().fullName() );
02499 }
02500 if ( !incidence->location().isEmpty() ) {
02501 body += i18n("Location: %1\n").arg( incidence->location() );
02502 }
02503 return body;
02504 }
02505
02506 bool IncidenceFormatter::MailBodyVisitor::visit( Event *event )
02507 {
02508 QString recurrence[]= {i18n("no recurrence", "None"),
02509 i18n("Minutely"), i18n("Hourly"), i18n("Daily"),
02510 i18n("Weekly"), i18n("Monthly Same Day"), i18n("Monthly Same Position"),
02511 i18n("Yearly"), i18n("Yearly"), i18n("Yearly")};
02512
02513 mResult = mailBodyIncidence( event );
02514 mResult += i18n("Start Date: %1\n").arg( event->dtStartDateStr() );
02515 if ( !event->doesFloat() ) {
02516 mResult += i18n("Start Time: %1\n").arg( event->dtStartTimeStr() );
02517 }
02518 if ( event->dtStart() != event->dtEnd() ) {
02519 mResult += i18n("End Date: %1\n").arg( event->dtEndDateStr() );
02520 }
02521 if ( !event->doesFloat() ) {
02522 mResult += i18n("End Time: %1\n").arg( event->dtEndTimeStr() );
02523 }
02524 if ( event->doesRecur() ) {
02525 Recurrence *recur = event->recurrence();
02526
02527 mResult += i18n("Recurs: %1\n")
02528 .arg( recurrence[ recur->recurrenceType() ] );
02529 mResult += i18n("Frequency: %1\n")
02530 .arg( event->recurrence()->frequency() );
02531
02532 if ( recur->duration() > 0 ) {
02533 mResult += i18n ("Repeats once", "Repeats %n times", recur->duration());
02534 mResult += '\n';
02535 } else {
02536 if ( recur->duration() != -1 ) {
02537
02538 QString endstr;
02539 if ( event->doesFloat() ) {
02540 endstr = KGlobal::locale()->formatDate( recur->endDate() );
02541 } else {
02542 endstr = KGlobal::locale()->formatDateTime( recur->endDateTime() );
02543 }
02544 mResult += i18n("Repeat until: %1\n").arg( endstr );
02545 } else {
02546 mResult += i18n("Repeats forever\n");
02547 }
02548 }
02549 }
02550 QString details = event->description();
02551 if ( !details.isEmpty() ) {
02552 mResult += i18n("Details:\n%1\n").arg( details );
02553 }
02554 return !mResult.isEmpty();
02555 }
02556
02557 bool IncidenceFormatter::MailBodyVisitor::visit( Todo *todo )
02558 {
02559 mResult = mailBodyIncidence( todo );
02560
02561 if ( todo->hasStartDate() ) {
02562 mResult += i18n("Start Date: %1\n").arg( todo->dtStartDateStr() );
02563 if ( !todo->doesFloat() ) {
02564 mResult += i18n("Start Time: %1\n").arg( todo->dtStartTimeStr() );
02565 }
02566 }
02567 if ( todo->hasDueDate() ) {
02568 mResult += i18n("Due Date: %1\n").arg( todo->dtDueDateStr() );
02569 if ( !todo->doesFloat() ) {
02570 mResult += i18n("Due Time: %1\n").arg( todo->dtDueTimeStr() );
02571 }
02572 }
02573 QString details = todo->description();
02574 if ( !details.isEmpty() ) {
02575 mResult += i18n("Details:\n%1\n").arg( details );
02576 }
02577 return !mResult.isEmpty();
02578 }
02579
02580 bool IncidenceFormatter::MailBodyVisitor::visit( Journal *journal )
02581 {
02582 mResult = mailBodyIncidence( journal );
02583 mResult += i18n("Date: %1\n").arg( journal->dtStartDateStr() );
02584 if ( !journal->doesFloat() ) {
02585 mResult += i18n("Time: %1\n").arg( journal->dtStartTimeStr() );
02586 }
02587 if ( !journal->description().isEmpty() )
02588 mResult += i18n("Text of the journal:\n%1\n").arg( journal->description() );
02589 return !mResult.isEmpty();
02590 }
02591
02592
02593
02594 QString IncidenceFormatter::mailBodyString( IncidenceBase *incidence )
02595 {
02596 if ( !incidence )
02597 return QString::null;
02598
02599 MailBodyVisitor v;
02600 if ( v.act( incidence ) ) {
02601 return v.result();
02602 }
02603 return QString::null;
02604 }
02605
02606 QString IncidenceFormatter::recurrenceString(Incidence * incidence)
02607 {
02608 if ( !incidence->doesRecur() )
02609 return i18n( "No recurrence" );
02610
02611
02612 QStringList dayList;
02613 dayList.append( i18n( "31st Last" ) );
02614 dayList.append( i18n( "30th Last" ) );
02615 dayList.append( i18n( "29th Last" ) );
02616 dayList.append( i18n( "28th Last" ) );
02617 dayList.append( i18n( "27th Last" ) );
02618 dayList.append( i18n( "26th Last" ) );
02619 dayList.append( i18n( "25th Last" ) );
02620 dayList.append( i18n( "24th Last" ) );
02621 dayList.append( i18n( "23rd Last" ) );
02622 dayList.append( i18n( "22nd Last" ) );
02623 dayList.append( i18n( "21st Last" ) );
02624 dayList.append( i18n( "20th Last" ) );
02625 dayList.append( i18n( "19th Last" ) );
02626 dayList.append( i18n( "18th Last" ) );
02627 dayList.append( i18n( "17th Last" ) );
02628 dayList.append( i18n( "16th Last" ) );
02629 dayList.append( i18n( "15th Last" ) );
02630 dayList.append( i18n( "14th Last" ) );
02631 dayList.append( i18n( "13th Last" ) );
02632 dayList.append( i18n( "12th Last" ) );
02633 dayList.append( i18n( "11th Last" ) );
02634 dayList.append( i18n( "10th Last" ) );
02635 dayList.append( i18n( "9th Last" ) );
02636 dayList.append( i18n( "8th Last" ) );
02637 dayList.append( i18n( "7th Last" ) );
02638 dayList.append( i18n( "6th Last" ) );
02639 dayList.append( i18n( "5th Last" ) );
02640 dayList.append( i18n( "4th Last" ) );
02641 dayList.append( i18n( "3rd Last" ) );
02642 dayList.append( i18n( "2nd Last" ) );
02643 dayList.append( i18n( "last day of the month", "Last" ) );
02644 dayList.append( i18n( "unknown day of the month", "unknown" ) );
02645 dayList.append( i18n( "1st" ) );
02646 dayList.append( i18n( "2nd" ) );
02647 dayList.append( i18n( "3rd" ) );
02648 dayList.append( i18n( "4th" ) );
02649 dayList.append( i18n( "5th" ) );
02650
02651 QString recurString;
02652 const KCalendarSystem *calSys = KGlobal::locale()->calendar();;
02653
02654 Recurrence *recurs = incidence->recurrence();
02655 switch ( recurs->recurrenceType() ) {
02656
02657 case Recurrence::rNone:
02658 recurString = i18n( "no recurrence", "None" );
02659 break;
02660 case Recurrence::rDaily:
02661 recurString = i18n( "Every day", "Every %1 days", recurs->frequency() );
02662 break;
02663 case Recurrence::rWeekly:
02664 {
02665 QString dayNames;
02666
02667 int weekStart = KGlobal::locale()->weekStartDay();
02668 bool addSpace = false;
02669 for ( int i = 0; i < 7; ++i ) {
02670 if ( recurs->days().testBit( (i+weekStart+6)%7 )) {
02671 if (addSpace) dayNames.append(" ");
02672 dayNames.append( calSys->weekDayName( ((i+weekStart+6)%7)+1, true ) );
02673 addSpace=true;
02674 }
02675 }
02676 recurString = i18n( "Every week on %1",
02677 "Every %n weeks on %1",
02678 recurs->frequency()).arg( dayNames );
02679 break;
02680 }
02681 case Recurrence::rMonthlyPos:
02682 {
02683 KCal::RecurrenceRule::WDayPos rule = recurs->monthPositions()[0];
02684 recurString = i18n( "Every month on the %1 %2",
02685 "Every %n months on the %1 %2",
02686 recurs->frequency() ).arg(dayList[rule.pos() + 31]).arg(
02687 calSys->weekDayName( rule.day(),false ) );
02688 break;
02689 }
02690 case Recurrence::rMonthlyDay:
02691 {
02692 int days = recurs->monthDays()[0];
02693 if (days < 0) {
02694 recurString = i18n( "Every month on the %1 day",
02695 "Every %n months on the %1 day",
02696 recurs->frequency() ).arg( dayList[days + 31] );
02697 } else {
02698 recurString = i18n( "Every month on day %1",
02699 "Every %n months on day %1",
02700 recurs->frequency() ).arg( recurs->monthDays()[0] );
02701 }
02702 break;
02703 }
02704
02705 case Recurrence::rYearlyMonth:
02706 {
02707 recurString = i18n( "Every year on day %1 of %2",
02708 "Every %n years on day %1 of %2",
02709 recurs->frequency() )
02710 .arg(recurs->yearDates()[0])
02711 .arg(calSys->monthName( recurs->yearMonths()[0], recurs->startDate().year() ) );
02712 break;
02713 }
02714 case Recurrence::rYearlyPos:
02715 {
02716 KCal::RecurrenceRule::WDayPos rule = recurs->yearPositions()[0];
02717 recurString = i18n( "Every year on the %1 %2 of %3",
02718 "Every %n years on the %1 %2of %3",
02719 recurs->frequency()).arg( dayList[rule.pos() + 31] )
02720 .arg( calSys->weekDayName( rule.day(), false ))
02721 .arg( calSys->monthName( recurs->yearMonths()[0], recurs->startDate().year() ) );
02722 break;
02723 }
02724 case Recurrence::rYearlyDay:
02725 {
02726 recurString = i18n( "Every year on day %1",
02727 "Every %n years on day %1",
02728 recurs->frequency()).arg( recurs->yearDays()[0] );
02729 break;
02730 }
02731
02732 default:
02733 return i18n( "Incidence recurs" );
02734 }
02735
02736 return recurString;
02737 }