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