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 if ( event->doesRecur() )
00704 html += invitationRow( i18n( "Recurrence:" ), IncidenceFormatter::recurrenceString( event ) );
00705
00706 html += "</table>\n";
00707 html += invitationsDetailsIncidence( event );
00708 html += "</div>\n";
00709
00710 return html;
00711 }
00712
00713 static QString invitationDetailsTodo( Todo *todo )
00714 {
00715
00716 if ( !todo )
00717 return QString::null;
00718
00719 QString sSummary = i18n( "Summary unspecified" );
00720 QString sDescr = i18n( "Description unspecified" );
00721 if ( ! todo->summary().isEmpty() ) {
00722 sSummary = todo->summary();
00723 }
00724 if ( ! todo->description().isEmpty() ) {
00725 sDescr = todo->description();
00726 }
00727 QString html( "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n" );
00728 html += invitationRow( i18n( "Summary:" ), sSummary );
00729 html += invitationRow( i18n( "Description:" ), sDescr );
00730 html += "</table>\n";
00731 html += invitationsDetailsIncidence( todo );
00732
00733 return html;
00734 }
00735
00736 static QString invitationDetailsJournal( Journal *journal )
00737 {
00738 if ( !journal )
00739 return QString::null;
00740
00741 QString sSummary = i18n( "Summary unspecified" );
00742 QString sDescr = i18n( "Description unspecified" );
00743 if ( ! journal->summary().isEmpty() ) {
00744 sSummary = journal->summary();
00745 }
00746 if ( ! journal->description().isEmpty() ) {
00747 sDescr = journal->description();
00748 }
00749 QString html( "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n" );
00750 html += invitationRow( i18n( "Summary:" ), sSummary );
00751 html += invitationRow( i18n( "Date:" ), journal->dtStartDateStr( false ) );
00752 html += invitationRow( i18n( "Description:" ), sDescr );
00753 html += "</table>\n";
00754 html += invitationsDetailsIncidence( journal );
00755
00756 return html;
00757 }
00758
00759 static QString invitationDetailsFreeBusy( FreeBusy *fb )
00760 {
00761 if ( !fb )
00762 return QString::null;
00763 QString html( "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n" );
00764
00765 html += invitationRow( i18n("Person:"), fb->organizer().fullName() );
00766 html += invitationRow( i18n("Start date:"), fb->dtStartDateStr() );
00767 html += invitationRow( i18n("End date:"),
00768 KGlobal::locale()->formatDate( fb->dtEnd().date(), true ) );
00769 html += "<tr><td colspan=2><hr></td></tr>\n";
00770 html += "<tr><td colspan=2>Busy periods given in this free/busy object:</td></tr>\n";
00771
00772 QValueList<Period> periods = fb->busyPeriods();
00773
00774 QValueList<Period>::iterator it;
00775 for ( it = periods.begin(); it != periods.end(); ++it ) {
00776 Period per = *it;
00777 if ( per.hasDuration() ) {
00778 int dur = per.duration().asSeconds();
00779 QString cont;
00780 if ( dur >= 3600 ) {
00781 cont += i18n("1 hour ", "%n hours ", dur / 3600);
00782 dur %= 3600;
00783 }
00784 if ( dur >= 60 ) {
00785 cont += i18n("1 minute", "%n minutes ", dur / 60);
00786 dur %= 60;
00787 }
00788 if ( dur > 0 ) {
00789 cont += i18n("1 second", "%n seconds", dur);
00790 }
00791 html += invitationRow( QString::null, i18n("startDate for duration", "%1 for %2")
00792 .arg( KGlobal::locale()->formatDateTime( per.start(), false ) )
00793 .arg(cont) );
00794 } else {
00795 QString cont;
00796 if ( per.start().date() == per.end().date() ) {
00797 cont = i18n("date, fromTime - toTime ", "%1, %2 - %3")
00798 .arg( KGlobal::locale()->formatDate( per.start().date() ) )
00799 .arg( KGlobal::locale()->formatTime( per.start().time() ) )
00800 .arg( KGlobal::locale()->formatTime( per.end().time() ) );
00801 } else {
00802 cont = i18n("fromDateTime - toDateTime", "%1 - %2")
00803 .arg( KGlobal::locale()->formatDateTime( per.start(), false ) )
00804 .arg( KGlobal::locale()->formatDateTime( per.end(), false ) );
00805 }
00806
00807 html += invitationRow( QString::null, cont );
00808 }
00809 }
00810
00811 html += "</table>\n";
00812 return html;
00813 }
00814
00815 static QString invitationHeaderEvent( Event *event, ScheduleMessage *msg )
00816 {
00817 if ( !msg || !event )
00818 return QString::null;
00819 switch ( msg->method() ) {
00820 case Scheduler::Publish:
00821 return i18n("This event has been published");
00822 case Scheduler::Request:
00823 if ( event->revision() > 0 )
00824 return i18n( "This meeting has been updated" );
00825 return i18n( "You have been invited to this meeting" );
00826 case Scheduler::Refresh:
00827 return i18n( "This invitation was refreshed" );
00828 case Scheduler::Cancel:
00829 return i18n( "This meeting has been canceled" );
00830 case Scheduler::Add:
00831 return i18n( "Addition to the meeting invitation" );
00832 case Scheduler::Reply: {
00833 Attendee::List attendees = event->attendees();
00834 if( attendees.count() == 0 ) {
00835 kdDebug(5850) << "No attendees in the iCal reply!\n";
00836 return QString::null;
00837 }
00838 if( attendees.count() != 1 )
00839 kdDebug(5850) << "Warning: attendeecount in the reply should be 1 "
00840 << "but is " << attendees.count() << endl;
00841 Attendee* attendee = *attendees.begin();
00842 QString attendeeName = attendee->name();
00843 if ( attendeeName.isEmpty() )
00844 attendeeName = attendee->email();
00845 if ( attendeeName.isEmpty() )
00846 attendeeName = i18n( "Sender" );
00847
00848 QString delegatorName, dummy;
00849 KPIM::getNameAndMail( attendee->delegator(), delegatorName, dummy );
00850 if ( delegatorName.isEmpty() )
00851 delegatorName = attendee->delegator();
00852
00853 switch( attendee->status() ) {
00854 case Attendee::NeedsAction:
00855 return i18n( "%1 indicates this invitation still needs some action" ).arg( attendeeName );
00856 case Attendee::Accepted:
00857 if ( delegatorName.isEmpty() )
00858 return i18n( "%1 accepts this meeting invitation" ).arg( attendeeName );
00859 return i18n( "%1 accepts this meeting invitation on behalf of %2" )
00860 .arg( attendeeName ).arg( delegatorName );
00861 case Attendee::Tentative:
00862 if ( delegatorName.isEmpty() )
00863 return i18n( "%1 tentatively accepts this meeting invitation" ).arg( attendeeName );
00864 return i18n( "%1 tentatively accepts this meeting invitation on behalf of %2" )
00865 .arg( attendeeName ).arg( delegatorName );
00866 case Attendee::Declined:
00867 if ( delegatorName.isEmpty() )
00868 return i18n( "%1 declines this meeting invitation" ).arg( attendeeName );
00869 return i18n( "%1 declines this meeting invitation on behalf of %2" )
00870 .arg( attendeeName ).arg( delegatorName );
00871 case Attendee::Delegated: {
00872 QString delegate, dummy;
00873 KPIM::getNameAndMail( attendee->delegate(), delegate, dummy );
00874 if ( delegate.isEmpty() )
00875 delegate = attendee->delegate();
00876 if ( !delegate.isEmpty() )
00877 return i18n( "%1 has delegated this meeting invitation to %2" )
00878 .arg( attendeeName ) .arg( delegate );
00879 return i18n( "%1 has delegated this meeting invitation" ).arg( attendeeName );
00880 }
00881 case Attendee::Completed:
00882 return i18n( "This meeting invitation is now completed" );
00883 case Attendee::InProcess:
00884 return i18n( "%1 is still processing the invitation" ).arg( attendeeName );
00885 default:
00886 return i18n( "Unknown response to this meeting invitation" );
00887 }
00888 break; }
00889 case Scheduler::Counter:
00890 return i18n( "Sender makes this counter proposal" );
00891 case Scheduler::Declinecounter:
00892 return i18n( "Sender declines the counter proposal" );
00893 case Scheduler::NoMethod:
00894 return i18n("Error: iMIP message with unknown method: '%1'")
00895 .arg( msg->method() );
00896 }
00897 return QString::null;
00898 }
00899
00900 static QString invitationHeaderTodo( Todo *todo, ScheduleMessage *msg )
00901 {
00902 if ( !msg || !todo )
00903 return QString::null;
00904 switch ( msg->method() ) {
00905 case Scheduler::Publish:
00906 return i18n("This task has been published");
00907 case Scheduler::Request:
00908 if ( todo->revision() > 0 )
00909 return i18n( "This task has been updated" );
00910 return i18n( "You have been assigned this task" );
00911 case Scheduler::Refresh:
00912 return i18n( "This task was refreshed" );
00913 case Scheduler::Cancel:
00914 return i18n( "This task was canceled" );
00915 case Scheduler::Add:
00916 return i18n( "Addition to the task" );
00917 case Scheduler::Reply: {
00918 Attendee::List attendees = todo->attendees();
00919 if( attendees.count() == 0 ) {
00920 kdDebug(5850) << "No attendees in the iCal reply!\n";
00921 return QString::null;
00922 }
00923 if( attendees.count() != 1 )
00924 kdDebug(5850) << "Warning: attendeecount in the reply should be 1 "
00925 << "but is " << attendees.count() << endl;
00926 Attendee* attendee = *attendees.begin();
00927
00928 switch( attendee->status() ) {
00929 case Attendee::NeedsAction:
00930 return i18n( "Sender indicates this task assignment still needs some action" );
00931 case Attendee::Accepted:
00932 return i18n( "Sender accepts this task" );
00933 case Attendee::Tentative:
00934 return i18n( "Sender tentatively accepts this task" );
00935 case Attendee::Declined:
00936 return i18n( "Sender declines this task" );
00937 case Attendee::Delegated: {
00938 QString delegate, dummy;
00939 KPIM::getNameAndMail( attendee->delegate(), delegate, dummy );
00940 if ( delegate.isEmpty() )
00941 delegate = attendee->delegate();
00942 if ( !delegate.isEmpty() )
00943 return i18n( "Sender has delegated this request for the task to %1" ).arg( delegate );
00944 return i18n( "Sender has delegated this request for the task " );
00945 }
00946 case Attendee::Completed:
00947 return i18n( "The request for this task is now completed" );
00948 case Attendee::InProcess:
00949 return i18n( "Sender is still processing the invitation" );
00950 default:
00951 return i18n( "Unknown response to this task" );
00952 }
00953 break; }
00954 case Scheduler::Counter:
00955 return i18n( "Sender makes this counter proposal" );
00956 case Scheduler::Declinecounter:
00957 return i18n( "Sender declines the counter proposal" );
00958 case Scheduler::NoMethod:
00959 return i18n("Error: iMIP message with unknown method: '%1'")
00960 .arg( msg->method() );
00961 }
00962 return QString::null;
00963 }
00964
00965 static QString invitationHeaderJournal( Journal *journal, ScheduleMessage *msg )
00966 {
00967
00968 if ( !msg || !journal )
00969 return QString::null;
00970 switch ( msg->method() ) {
00971 case Scheduler::Publish:
00972 return i18n("This journal has been published");
00973 case Scheduler::Request:
00974 return i18n( "You have been assigned this journal" );
00975 case Scheduler::Refresh:
00976 return i18n( "This journal was refreshed" );
00977 case Scheduler::Cancel:
00978 return i18n( "This journal was canceled" );
00979 case Scheduler::Add:
00980 return i18n( "Addition to the journal" );
00981 case Scheduler::Reply: {
00982 Attendee::List attendees = journal->attendees();
00983 if( attendees.count() == 0 ) {
00984 kdDebug(5850) << "No attendees in the iCal reply!\n";
00985 return QString::null;
00986 }
00987 if( attendees.count() != 1 )
00988 kdDebug(5850) << "Warning: attendeecount in the reply should be 1 "
00989 << "but is " << attendees.count() << endl;
00990 Attendee* attendee = *attendees.begin();
00991
00992 switch( attendee->status() ) {
00993 case Attendee::NeedsAction:
00994 return i18n( "Sender indicates this journal assignment still needs some action" );
00995 case Attendee::Accepted:
00996 return i18n( "Sender accepts this journal" );
00997 case Attendee::Tentative:
00998 return i18n( "Sender tentatively accepts this journal" );
00999 case Attendee::Declined:
01000 return i18n( "Sender declines this journal" );
01001 case Attendee::Delegated:
01002 return i18n( "Sender has delegated this request for the journal" );
01003 case Attendee::Completed:
01004 return i18n( "The request for this journal is now completed" );
01005 case Attendee::InProcess:
01006 return i18n( "Sender is still processing the invitation" );
01007 default:
01008 return i18n( "Unknown response to this journal" );
01009 }
01010 break; }
01011 case Scheduler::Counter:
01012 return i18n( "Sender makes this counter proposal" );
01013 case Scheduler::Declinecounter:
01014 return i18n( "Sender declines the counter proposal" );
01015 case Scheduler::NoMethod:
01016 return i18n("Error: iMIP message with unknown method: '%1'")
01017 .arg( msg->method() );
01018 }
01019 return QString::null;
01020 }
01021
01022 static QString invitationHeaderFreeBusy( FreeBusy *fb, ScheduleMessage *msg )
01023 {
01024 if ( !msg || !fb )
01025 return QString::null;
01026 switch ( msg->method() ) {
01027 case Scheduler::Publish:
01028 return i18n("This free/busy list has been published");
01029 case Scheduler::Request:
01030 return i18n( "The free/busy list has been requested" );
01031 case Scheduler::Refresh:
01032 return i18n( "This free/busy list was refreshed" );
01033 case Scheduler::Cancel:
01034 return i18n( "This free/busy list was canceled" );
01035 case Scheduler::Add:
01036 return i18n( "Addition to the free/busy list" );
01037 case Scheduler::NoMethod:
01038 default:
01039 return i18n("Error: Free/Busy iMIP message with unknown method: '%1'")
01040 .arg( msg->method() );
01041 }
01042 }
01043
01044 class IncidenceFormatter::ScheduleMessageVisitor : public IncidenceBase::Visitor
01045 {
01046 public:
01047 ScheduleMessageVisitor() : mMessage(0) { mResult = ""; }
01048 bool act( IncidenceBase *incidence, ScheduleMessage *msg ) { mMessage = msg; return incidence->accept( *this ); }
01049 QString result() const { return mResult; }
01050
01051 protected:
01052 QString mResult;
01053 ScheduleMessage *mMessage;
01054 };
01055
01056 class IncidenceFormatter::InvitationHeaderVisitor :
01057 public IncidenceFormatter::ScheduleMessageVisitor
01058 {
01059 protected:
01060 bool visit( Event *event )
01061 {
01062 mResult = invitationHeaderEvent( event, mMessage );
01063 return !mResult.isEmpty();
01064 }
01065 bool visit( Todo *todo )
01066 {
01067 mResult = invitationHeaderTodo( todo, mMessage );
01068 return !mResult.isEmpty();
01069 }
01070 bool visit( Journal *journal )
01071 {
01072 mResult = invitationHeaderJournal( journal, mMessage );
01073 return !mResult.isEmpty();
01074 }
01075 bool visit( FreeBusy *fb )
01076 {
01077 mResult = invitationHeaderFreeBusy( fb, mMessage );
01078 return !mResult.isEmpty();
01079 }
01080 };
01081
01082 class IncidenceFormatter::InvitationBodyVisitor :
01083 public IncidenceFormatter::ScheduleMessageVisitor
01084 {
01085 protected:
01086 bool visit( Event *event )
01087 {
01088 mResult = invitationDetailsEvent( event );
01089 return !mResult.isEmpty();
01090 }
01091 bool visit( Todo *todo )
01092 {
01093 mResult = invitationDetailsTodo( todo );
01094 return !mResult.isEmpty();
01095 }
01096 bool visit( Journal *journal )
01097 {
01098 mResult = invitationDetailsJournal( journal );
01099 return !mResult.isEmpty();
01100 }
01101 bool visit( FreeBusy *fb )
01102 {
01103 mResult = invitationDetailsFreeBusy( fb );
01104 return !mResult.isEmpty();
01105 }
01106 };
01107
01108 class IncidenceFormatter::IncidenceCompareVisitor :
01109 public IncidenceBase::Visitor
01110 {
01111 public:
01112 IncidenceCompareVisitor() : mExistingIncidence(0) {}
01113 bool act( IncidenceBase *incidence, Incidence* existingIncidence )
01114 {
01115 Incidence *inc = dynamic_cast<Incidence*>( incidence );
01116 if ( !inc || !existingIncidence || inc->revision() <= existingIncidence->revision() )
01117 return false;
01118 mExistingIncidence = existingIncidence;
01119 return incidence->accept( *this );
01120 }
01121
01122 QString result() const
01123 {
01124 if ( mChanges.isEmpty() )
01125 return QString();
01126 QString html = "<div align=\"left\"><ul><li>";
01127 html += mChanges.join( "</li><li>" );
01128 html += "</li><ul></div>";
01129 return html;
01130 }
01131
01132 protected:
01133 bool visit( Event *event )
01134 {
01135 compareEvents( event, dynamic_cast<Event*>( mExistingIncidence ) );
01136 compareIncidences( event, mExistingIncidence );
01137 return !mChanges.isEmpty();
01138 }
01139 bool visit( Todo *todo )
01140 {
01141 compareIncidences( todo, mExistingIncidence );
01142 return !mChanges.isEmpty();
01143 }
01144 bool visit( Journal *journal )
01145 {
01146 compareIncidences( journal, mExistingIncidence );
01147 return !mChanges.isEmpty();
01148 }
01149 bool visit( FreeBusy *fb )
01150 {
01151 Q_UNUSED( fb );
01152 return !mChanges.isEmpty();
01153 }
01154
01155 private:
01156 void compareEvents( Event *newEvent, Event *oldEvent )
01157 {
01158 if ( !oldEvent || !newEvent )
01159 return;
01160 if ( oldEvent->dtStart() != newEvent->dtStart() || oldEvent->doesFloat() != newEvent->doesFloat() )
01161 mChanges += i18n( "The begin of the meeting has been changed from %1 to %2" )
01162 .arg( eventStartTimeStr( oldEvent ) ).arg( eventStartTimeStr( newEvent ) );
01163 if ( oldEvent->dtEnd() != newEvent->dtEnd() || oldEvent->doesFloat() != newEvent->doesFloat() )
01164 mChanges += i18n( "The end of the meeting has been changed from %1 to %2" )
01165 .arg( eventEndTimeStr( oldEvent ) ).arg( eventEndTimeStr( newEvent ) );
01166 }
01167
01168 void compareIncidences( Incidence *newInc, Incidence *oldInc )
01169 {
01170 if ( !oldInc || !newInc )
01171 return;
01172 if ( oldInc->summary() != newInc->summary() )
01173 mChanges += i18n( "The summary has been changed to: \"%1\"" ).arg( newInc->summary() );
01174 if ( oldInc->location() != newInc->location() )
01175 mChanges += i18n( "The location has been changed to: \"%1\"" ).arg( newInc->location() );
01176 if ( oldInc->description() != newInc->description() )
01177 mChanges += i18n( "The description has been changed to: \"%1\"" ).arg( newInc->description() );
01178 Attendee::List oldAttendees = oldInc->attendees();
01179 Attendee::List newAttendees = newInc->attendees();
01180 for ( Attendee::List::ConstIterator it = newAttendees.constBegin(); it != newAttendees.constEnd(); ++it ) {
01181 Attendee *oldAtt = oldInc->attendeeByMail( (*it)->email() );
01182 if ( !oldAtt ) {
01183 mChanges += i18n( "Attendee %1 has been added" ).arg( (*it)->fullName() );
01184 } else {
01185 if ( oldAtt->status() != (*it)->status() )
01186 mChanges += i18n( "The status of attendee %1 has been changed to: %2" ).arg( (*it)->fullName() )
01187 .arg( (*it)->statusStr() );
01188 }
01189 }
01190 for ( Attendee::List::ConstIterator it = oldAttendees.constBegin(); it != oldAttendees.constEnd(); ++it ) {
01191 Attendee *newAtt = newInc->attendeeByMail( (*it)->email() );
01192 if ( !newAtt )
01193 mChanges += i18n( "Attendee %1 has been removed" ).arg( (*it)->fullName() );
01194 }
01195 }
01196
01197 private:
01198 Incidence* mExistingIncidence;
01199 QStringList mChanges;
01200 };
01201
01202
01203 QString InvitationFormatterHelper::makeLink( const QString &id, const QString &text )
01204 {
01205 QString res( "<a href=\"%1\"><b>%2</b></a>" );
01206 return res.arg( generateLinkURL( id ) ).arg( text );
01207 return res;
01208 }
01209
01210
01211
01212 static bool incidenceOwnedByMe( Calendar* calendar, Incidence *incidence )
01213 {
01214 CalendarResources* cal = dynamic_cast<CalendarResources*>( calendar );
01215 if ( !cal || !incidence )
01216 return true;
01217 ResourceCalendar* res = cal->resource( incidence );
01218 if ( !res )
01219 return true;
01220 const QString subRes = res->subresourceIdentifier( incidence );
01221 if ( !subRes.contains( "/.INBOX.directory/" ) )
01222 return false;
01223 return true;
01224 }
01225
01226 QString IncidenceFormatter::formatICalInvitation( QString invitation, Calendar *mCalendar,
01227 InvitationFormatterHelper *helper )
01228 {
01229 if ( invitation.isEmpty() ) return QString::null;
01230
01231 ICalFormat format;
01232
01233 ScheduleMessage *msg = format.parseScheduleMessage( mCalendar, invitation );
01234
01235 if( !msg ) {
01236 kdDebug( 5850 ) << "Failed to parse the scheduling message" << endl;
01237 Q_ASSERT( format.exception() );
01238 kdDebug( 5850 ) << format.exception()->message() << endl;
01239 return QString::null;
01240 }
01241
01242 IncidenceBase *incBase = msg->event();
01243
01244 Incidence* existingIncidence = 0;
01245 if ( helper->calendar() ) {
01246 existingIncidence = helper->calendar()->incidence( incBase->uid() );
01247 if ( !incidenceOwnedByMe( helper->calendar(), existingIncidence ) )
01248 existingIncidence = 0;
01249 if ( !existingIncidence ) {
01250 const Incidence::List list = helper->calendar()->incidences();
01251 for ( Incidence::List::ConstIterator it = list.begin(), end = list.end(); it != end; ++it ) {
01252 if ( (*it)->schedulingID() == incBase->uid() && incidenceOwnedByMe( helper->calendar(), *it ) ) {
01253 existingIncidence = *it;
01254 break;
01255 }
01256 }
01257 }
01258 }
01259
01260
01261 QString html;
01262
01263 QString tableStyle = QString::fromLatin1(
01264 "style=\"border: solid 1px; margin: 0em;\"" );
01265 QString tableHead = QString::fromLatin1(
01266 "<div align=\"center\">"
01267 "<table width=\"80%\" cellpadding=\"1\" cellspacing=\"0\" %1>"
01268 "<tr><td>").arg(tableStyle);
01269
01270 html += tableHead;
01271 InvitationHeaderVisitor headerVisitor;
01272
01273 if ( !headerVisitor.act( incBase, msg ) )
01274 return QString::null;
01275 html += "<b>" + headerVisitor.result() + "</b>";
01276
01277 InvitationBodyVisitor bodyVisitor;
01278 if ( !bodyVisitor.act( incBase, msg ) )
01279 return QString::null;
01280 html += bodyVisitor.result();
01281
01282 if ( msg->method() == Scheduler::Request ) {
01283 IncidenceCompareVisitor compareVisitor;
01284 if ( compareVisitor.act( incBase, existingIncidence ) ) {
01285 html += i18n("<p align=\"left\">The following changes have been made by the organizer:</p>");
01286 html += compareVisitor.result();
01287 }
01288 }
01289
01290 html += "<br/>";
01291 html += "<table border=\"0\" cellspacing=\"0\"><tr><td> </td></tr><tr>";
01292
01293 #if 0
01294 html += helper->makeLinkURL( "accept", i18n("[Enter this into my calendar]") );
01295 html += "</td><td> </td><td>";
01296 #endif
01297
01298
01299
01300 switch ( msg->method() ) {
01301 case Scheduler::Publish:
01302 case Scheduler::Request:
01303 case Scheduler::Refresh:
01304 case Scheduler::Add:
01305 {
01306 Incidence *inc = dynamic_cast<Incidence*>( incBase );
01307 if ( inc && inc->revision() > 0 && (existingIncidence || !helper->calendar()) ) {
01308 if ( incBase->type() == "Todo" ) {
01309 html += "<td colspan=\"9\">";
01310 html += helper->makeLink( "reply", i18n( "[Enter this into my task list]" ) );
01311 } else {
01312 html += "<td colspan=\"13\">";
01313 html += helper->makeLink( "reply", i18n( "[Enter this into my calendar]" ) );
01314 }
01315 html += "</td></tr><tr>";
01316 }
01317 html += "<td>";
01318
01319 if ( !existingIncidence ) {
01320
01321 html += helper->makeLink( "accept", i18n( "[Accept]" ) );
01322 html += "</td><td> </td><td>";
01323 html += helper->makeLink( "accept_conditionally",
01324 i18n( "Accept conditionally", "[Accept cond.]" ) );
01325 html += "</td><td> </td><td>";
01326
01327 html += helper->makeLink( "counter", i18n( "[Counter proposal]" ) );
01328 html += "</td><td> </td><td>";
01329
01330 html += helper->makeLink( "decline", i18n( "[Decline]" ) );
01331 html += "</td><td> </td><td>";
01332
01333
01334 html += helper->makeLink( "delegate", i18n( "[Delegate]" ) );
01335 html += "</td><td> </td><td>";
01336
01337
01338 html += helper->makeLink( "forward", i18n( "[Forward]" ) );
01339
01340 if ( incBase->type() == "Event" ) {
01341 html += "</b></a></td><td> </td><td>";
01342 html += helper->makeLink( "check_calendar", i18n("[Check my calendar]" ) );
01343 }
01344 }
01345 break;
01346 }
01347
01348 case Scheduler::Cancel:
01349
01350 html += helper->makeLink( "cancel", i18n( "[Remove this from my calendar]" ) );
01351 break;
01352
01353 case Scheduler::Reply:
01354
01355 if ( incBase->type() == "Todo" ) {
01356 html += helper->makeLink( "reply", i18n( "[Enter this into my task list]" ) );
01357 } else {
01358 html += helper->makeLink( "reply", i18n( "[Enter this into my calendar]" ) );
01359 }
01360 break;
01361
01362 case Scheduler::Counter:
01363 html += helper->makeLink( "accept_counter", i18n("[Accept]") );
01364 html += " ";
01365 html += helper->makeLink( "decline_counter", i18n("[Decline]") );
01366 html += " ";
01367 html += helper->makeLink( "check_calendar", i18n("[Check my calendar]" ) );
01368 break;
01369 case Scheduler::Declinecounter:
01370 case Scheduler::NoMethod:
01371 break;
01372 }
01373
01374 html += "</td></tr></table>";
01375
01376 html += "</td></tr></table><br></div>";
01377
01378 return html;
01379 }
01380
01381
01382
01383
01384
01385
01386
01387
01388
01389
01390
01391 static QString stringProp( KTNEFMessage* tnefMsg, const Q_UINT32& key,
01392 const QString& fallback = QString::null)
01393 {
01394 return tnefMsg->findProp( key < 0x10000 ? key & 0xFFFF : key >> 16,
01395 fallback );
01396 }
01397
01398 static QString sNamedProp( KTNEFMessage* tnefMsg, const QString& name,
01399 const QString& fallback = QString::null )
01400 {
01401 return tnefMsg->findNamedProp( name, fallback );
01402 }
01403
01404 struct save_tz { char* old_tz; char* tz_env_str; };
01405
01406
01407 static struct save_tz set_tz( const char* _tc )
01408 {
01409 const char *tc = _tc?_tc:"UTC";
01410
01411 struct save_tz rv;
01412
01413 rv.old_tz = 0;
01414 rv.tz_env_str = 0;
01415
01416
01417
01418 char* tz_env = 0;
01419 if( getenv( "TZ" ) ) {
01420 tz_env = strdup( getenv( "TZ" ) );
01421 rv.old_tz = tz_env;
01422 }
01423 char* tmp_env = (char*)malloc( strlen( tc ) + 4 );
01424 strcpy( tmp_env, "TZ=" );
01425 strcpy( tmp_env+3, tc );
01426 putenv( tmp_env );
01427
01428 rv.tz_env_str = tmp_env;
01429
01430
01431
01432 tzset();
01433
01434
01435 return rv;
01436 }
01437
01438
01439 static void unset_tz( struct save_tz old_tz )
01440 {
01441 if( old_tz.old_tz ) {
01442 char* tmp_env = (char*)malloc( strlen( old_tz.old_tz ) + 4 );
01443 strcpy( tmp_env, "TZ=" );
01444 strcpy( tmp_env+3, old_tz.old_tz );
01445 putenv( tmp_env );
01446
01447 free( old_tz.old_tz );
01448 } else {
01449
01450 putenv( strdup("TZ") );
01451 }
01452 tzset();
01453
01454
01455 if( old_tz.tz_env_str ) free( old_tz.tz_env_str );
01456 }
01457
01458 static QDateTime utc2Local( const QDateTime& utcdt )
01459 {
01460 struct tm tmL;
01461
01462 save_tz tmp_tz = set_tz("UTC");
01463 time_t utc = utcdt.toTime_t();
01464 unset_tz( tmp_tz );
01465
01466 localtime_r( &utc, &tmL );
01467 return QDateTime( QDate( tmL.tm_year+1900, tmL.tm_mon+1, tmL.tm_mday ),
01468 QTime( tmL.tm_hour, tmL.tm_min, tmL.tm_sec ) );
01469 }
01470
01471
01472 static QDateTime pureISOToLocalQDateTime( const QString& dtStr,
01473 bool bDateOnly = false )
01474 {
01475 QDate tmpDate;
01476 QTime tmpTime;
01477 int year, month, day, hour, minute, second;
01478
01479 if( bDateOnly ) {
01480 year = dtStr.left( 4 ).toInt();
01481 month = dtStr.mid( 4, 2 ).toInt();
01482 day = dtStr.mid( 6, 2 ).toInt();
01483 hour = 0;
01484 minute = 0;
01485 second = 0;
01486 } else {
01487 year = dtStr.left( 4 ).toInt();
01488 month = dtStr.mid( 4, 2 ).toInt();
01489 day = dtStr.mid( 6, 2 ).toInt();
01490 hour = dtStr.mid( 9, 2 ).toInt();
01491 minute = dtStr.mid( 11, 2 ).toInt();
01492 second = dtStr.mid( 13, 2 ).toInt();
01493 }
01494 tmpDate.setYMD( year, month, day );
01495 tmpTime.setHMS( hour, minute, second );
01496
01497 if( tmpDate.isValid() && tmpTime.isValid() ) {
01498 QDateTime dT = QDateTime( tmpDate, tmpTime );
01499
01500 if( !bDateOnly ) {
01501
01502 if (dtStr.at(dtStr.length()-1) == 'Z') {
01503
01504
01505 dT = utc2Local( dT );
01506 }
01507 }
01508 return dT;
01509 } else
01510 return QDateTime();
01511 }
01512
01513
01514
01515 QString IncidenceFormatter::msTNEFToVPart( const QByteArray& tnef )
01516 {
01517 bool bOk = false;
01518
01519 KTNEFParser parser;
01520 QBuffer buf( tnef );
01521 CalendarLocal cal ( QString::fromLatin1( "UTC" ) );
01522 KABC::Addressee addressee;
01523 KABC::VCardConverter cardConv;
01524 ICalFormat calFormat;
01525 Event* event = new Event();
01526
01527 if( parser.openDevice( &buf ) ) {
01528 KTNEFMessage* tnefMsg = parser.message();
01529
01530
01531
01532
01533 QString msgClass = tnefMsg->findProp( 0x001A, QString::null, true )
01534 .upper();
01535 if( !msgClass.isEmpty() ) {
01536
01537
01538 bool bCompatClassAppointment = false;
01539 bool bCompatMethodRequest = false;
01540 bool bCompatMethodCancled = false;
01541 bool bCompatMethodAccepted = false;
01542 bool bCompatMethodAcceptedCond = false;
01543 bool bCompatMethodDeclined = false;
01544 if( msgClass.startsWith( "IPM.MICROSOFT SCHEDULE." ) ) {
01545 bCompatClassAppointment = true;
01546 if( msgClass.endsWith( ".MTGREQ" ) )
01547 bCompatMethodRequest = true;
01548 if( msgClass.endsWith( ".MTGCNCL" ) )
01549 bCompatMethodCancled = true;
01550 if( msgClass.endsWith( ".MTGRESPP" ) )
01551 bCompatMethodAccepted = true;
01552 if( msgClass.endsWith( ".MTGRESPA" ) )
01553 bCompatMethodAcceptedCond = true;
01554 if( msgClass.endsWith( ".MTGRESPN" ) )
01555 bCompatMethodDeclined = true;
01556 }
01557 bool bCompatClassNote = ( msgClass == "IPM.MICROSOFT MAIL.NOTE" );
01558
01559 if( bCompatClassAppointment || "IPM.APPOINTMENT" == msgClass ) {
01560
01561 bool bIsReply = false;
01562 QString prodID = "-//Microsoft Corporation//Outlook ";
01563 prodID += tnefMsg->findNamedProp( "0x8554", "9.0" );
01564 prodID += "MIMEDIR/EN\n";
01565 prodID += "VERSION:2.0\n";
01566 calFormat.setApplication( "Outlook", prodID );
01567
01568 Scheduler::Method method;
01569 if( bCompatMethodRequest )
01570 method = Scheduler::Request;
01571 else if( bCompatMethodCancled )
01572 method = Scheduler::Cancel;
01573 else if( bCompatMethodAccepted || bCompatMethodAcceptedCond ||
01574 bCompatMethodDeclined ) {
01575 method = Scheduler::Reply;
01576 bIsReply = true;
01577 } else {
01578
01579
01580
01581
01582
01583
01584
01585
01586 if( tnefMsg->findProp(0x0c17) == "1" )
01587 bIsReply = true;
01588 method = Scheduler::Request;
01589 }
01590
01592 ScheduleMessage schedMsg(event, method, ScheduleMessage::Unknown );
01593
01594 QString sSenderSearchKeyEmail( tnefMsg->findProp( 0x0C1D ) );
01595
01596 if( !sSenderSearchKeyEmail.isEmpty() ) {
01597 int colon = sSenderSearchKeyEmail.find( ':' );
01598
01599 if( sSenderSearchKeyEmail.find( ':' ) == -1 )
01600 sSenderSearchKeyEmail.remove( 0, colon+1 );
01601 }
01602
01603 QString s( tnefMsg->findProp( 0x0e04 ) );
01604 QStringList attendees = QStringList::split( ';', s );
01605 if( attendees.count() ) {
01606 for( QStringList::Iterator it = attendees.begin();
01607 it != attendees.end(); ++it ) {
01608
01609
01610 if( (*it).find('@') == -1 ) {
01611 s = (*it).stripWhiteSpace();
01612
01613 Attendee *attendee = new Attendee( s, s, true );
01614 if( bIsReply ) {
01615 if( bCompatMethodAccepted )
01616 attendee->setStatus( Attendee::Accepted );
01617 if( bCompatMethodDeclined )
01618 attendee->setStatus( Attendee::Declined );
01619 if( bCompatMethodAcceptedCond )
01620 attendee->setStatus(Attendee::Tentative);
01621 } else {
01622 attendee->setStatus( Attendee::NeedsAction );
01623 attendee->setRole( Attendee::ReqParticipant );
01624 }
01625 event->addAttendee(attendee);
01626 }
01627 }
01628 } else {
01629
01630
01631 s = sSenderSearchKeyEmail;
01632 if( !s.isEmpty() ) {
01633 Attendee *attendee = new Attendee( QString::null, QString::null,
01634 true );
01635 if( bIsReply ) {
01636 if( bCompatMethodAccepted )
01637 attendee->setStatus( Attendee::Accepted );
01638 if( bCompatMethodAcceptedCond )
01639 attendee->setStatus( Attendee::Declined );
01640 if( bCompatMethodDeclined )
01641 attendee->setStatus( Attendee::Tentative );
01642 } else {
01643 attendee->setStatus(Attendee::NeedsAction);
01644 attendee->setRole(Attendee::ReqParticipant);
01645 }
01646 event->addAttendee(attendee);
01647 }
01648 }
01649 s = tnefMsg->findProp( 0x0c1f );
01650 if( s.isEmpty() && !bIsReply )
01651 s = sSenderSearchKeyEmail;
01652
01653 if( !s.isEmpty() )
01654 event->setOrganizer( s );
01655
01656 s = tnefMsg->findProp( 0x8516 ).replace( QChar( '-' ), QString::null )
01657 .replace( QChar( ':' ), QString::null );
01658 event->setDtStart( QDateTime::fromString( s ) );
01659
01660 s = tnefMsg->findProp( 0x8517 ).replace( QChar( '-' ), QString::null )
01661 .replace( QChar( ':' ), QString::null );
01662 event->setDtEnd( QDateTime::fromString( s ) );
01663
01664 s = tnefMsg->findProp( 0x8208 );
01665 event->setLocation( s );
01666
01667
01668
01669
01670
01671
01672 s = tnefMsg->findProp( 0x0023 );
01673 event->setUid( s );
01674
01675
01676
01677
01678 s = tnefMsg->findProp( 0x8202 ).replace( QChar( '-' ), QString::null )
01679 .replace( QChar( ':' ), QString::null );
01680
01681
01682
01683 s = tnefMsg->findNamedProp( "Keywords" );
01684 event->setCategories( s );
01685
01686 s = tnefMsg->findProp( 0x1000 );
01687 event->setDescription( s );
01688
01689 s = tnefMsg->findProp( 0x0070 );
01690 event->setSummary( s );
01691
01692 s = tnefMsg->findProp( 0x0026 );
01693 event->setPriority( s.toInt() );
01694
01695
01696 if(!tnefMsg->findProp(0x8503).isEmpty()) {
01697 Alarm *alarm = new Alarm(event);
01698 QDateTime highNoonTime =
01699 pureISOToLocalQDateTime( tnefMsg->findProp( 0x8502 )
01700 .replace( QChar( '-' ), "" )
01701 .replace( QChar( ':' ), "" ) );
01702 QDateTime wakeMeUpTime =
01703 pureISOToLocalQDateTime( tnefMsg->findProp( 0x8560, "" )
01704 .replace( QChar( '-' ), "" )
01705 .replace( QChar( ':' ), "" ) );
01706 alarm->setTime(wakeMeUpTime);
01707
01708 if( highNoonTime.isValid() && wakeMeUpTime.isValid() )
01709 alarm->setStartOffset( Duration( highNoonTime, wakeMeUpTime ) );
01710 else
01711
01712 alarm->setStartOffset( Duration( 15*60 ) );
01713 alarm->setDisplayAlarm( i18n( "Reminder" ) );
01714
01715
01716
01717 event->addAlarm( alarm );
01718 }
01719 cal.addEvent( event );
01720 bOk = true;
01721
01722 } else if( bCompatClassNote || "IPM.CONTACT" == msgClass ) {
01723 addressee.setUid( stringProp( tnefMsg, attMSGID ) );
01724 addressee.setFormattedName( stringProp( tnefMsg, MAPI_TAG_PR_DISPLAY_NAME ) );
01725 addressee.insertEmail( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_EMAIL1EMAILADDRESS ), true );
01726 addressee.insertEmail( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_EMAIL2EMAILADDRESS ), false );
01727 addressee.insertEmail( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_EMAIL3EMAILADDRESS ), false );
01728 addressee.insertCustom( "KADDRESSBOOK", "X-IMAddress", sNamedProp( tnefMsg, MAPI_TAG_CONTACT_IMADDRESS ) );
01729 addressee.insertCustom( "KADDRESSBOOK", "X-SpousesName", stringProp( tnefMsg, MAPI_TAG_PR_SPOUSE_NAME ) );
01730 addressee.insertCustom( "KADDRESSBOOK", "X-ManagersName", stringProp( tnefMsg, MAPI_TAG_PR_MANAGER_NAME ) );
01731 addressee.insertCustom( "KADDRESSBOOK", "X-AssistantsName", stringProp( tnefMsg, MAPI_TAG_PR_ASSISTANT ) );
01732 addressee.insertCustom( "KADDRESSBOOK", "X-Department", stringProp( tnefMsg, MAPI_TAG_PR_DEPARTMENT_NAME ) );
01733 addressee.insertCustom( "KADDRESSBOOK", "X-Office", stringProp( tnefMsg, MAPI_TAG_PR_OFFICE_LOCATION ) );
01734 addressee.insertCustom( "KADDRESSBOOK", "X-Profession", stringProp( tnefMsg, MAPI_TAG_PR_PROFESSION ) );
01735
01736 QString s = tnefMsg->findProp( MAPI_TAG_PR_WEDDING_ANNIVERSARY )
01737 .replace( QChar( '-' ), QString::null )
01738 .replace( QChar( ':' ), QString::null );
01739 if( !s.isEmpty() )
01740 addressee.insertCustom( "KADDRESSBOOK", "X-Anniversary", s );
01741
01742 addressee.setUrl( KURL( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_WEBPAGE ) ) );
01743
01744
01745 addressee.setFamilyName( stringProp( tnefMsg, MAPI_TAG_PR_SURNAME ) );
01746 addressee.setGivenName( stringProp( tnefMsg, MAPI_TAG_PR_GIVEN_NAME ) );
01747 addressee.setAdditionalName( stringProp( tnefMsg, MAPI_TAG_PR_MIDDLE_NAME ) );
01748 addressee.setPrefix( stringProp( tnefMsg, MAPI_TAG_PR_DISPLAY_NAME_PREFIX ) );
01749 addressee.setSuffix( stringProp( tnefMsg, MAPI_TAG_PR_GENERATION ) );
01750
01751 addressee.setNickName( stringProp( tnefMsg, MAPI_TAG_PR_NICKNAME ) );
01752 addressee.setRole( stringProp( tnefMsg, MAPI_TAG_PR_TITLE ) );
01753 addressee.setOrganization( stringProp( tnefMsg, MAPI_TAG_PR_COMPANY_NAME ) );
01754
01755
01756
01757
01758
01759 KABC::Address adr;
01760 adr.setPostOfficeBox( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_PO_BOX ) );
01761 adr.setStreet( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_STREET ) );
01762 adr.setLocality( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_CITY ) );
01763 adr.setRegion( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_STATE_OR_PROVINCE ) );
01764 adr.setPostalCode( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_POSTAL_CODE ) );
01765 adr.setCountry( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_COUNTRY ) );
01766 adr.setType(KABC::Address::Home);
01767 addressee.insertAddress(adr);
01768
01769 adr.setPostOfficeBox( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSPOBOX ) );
01770 adr.setStreet( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSSTREET ) );
01771 adr.setLocality( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSCITY ) );
01772 adr.setRegion( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSSTATE ) );
01773 adr.setPostalCode( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSPOSTALCODE ) );
01774 adr.setCountry( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSCOUNTRY ) );
01775 adr.setType( KABC::Address::Work );
01776 addressee.insertAddress( adr );
01777
01778 adr.setPostOfficeBox( stringProp( tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_PO_BOX ) );
01779 adr.setStreet( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_STREET ) );
01780 adr.setLocality( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_CITY ) );
01781 adr.setRegion( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_STATE_OR_PROVINCE ) );
01782 adr.setPostalCode( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_POSTAL_CODE ) );
01783 adr.setCountry( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_COUNTRY ) );
01784 adr.setType( KABC::Address::Dom );
01785 addressee.insertAddress(adr);
01786
01787
01788
01789
01790
01791 QString nr;
01792 nr = stringProp( tnefMsg, MAPI_TAG_PR_HOME_TELEPHONE_NUMBER );
01793 addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Home ) );
01794 nr = stringProp( tnefMsg, MAPI_TAG_PR_BUSINESS_TELEPHONE_NUMBER );
01795 addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Work ) );
01796 nr = stringProp( tnefMsg, MAPI_TAG_PR_MOBILE_TELEPHONE_NUMBER );
01797 addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Cell ) );
01798 nr = stringProp( tnefMsg, MAPI_TAG_PR_HOME_FAX_NUMBER );
01799 addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Fax | KABC::PhoneNumber::Home ) );
01800 nr = stringProp( tnefMsg, MAPI_TAG_PR_BUSINESS_FAX_NUMBER );
01801 addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Fax | KABC::PhoneNumber::Work ) );
01802
01803 s = tnefMsg->findProp( MAPI_TAG_PR_BIRTHDAY )
01804 .replace( QChar( '-' ), QString::null )
01805 .replace( QChar( ':' ), QString::null );
01806 if( !s.isEmpty() )
01807 addressee.setBirthday( QDateTime::fromString( s ) );
01808
01809 bOk = ( !addressee.isEmpty() );
01810 } else if( "IPM.NOTE" == msgClass ) {
01811
01812 }
01813 }
01814 }
01815
01816
01817 QString iCal = calFormat.toString( &cal );
01818 if( !iCal.isEmpty() )
01819
01820 return iCal;
01821
01822
01823 KABC::VCardConverter converter;
01824 return converter.createVCard( addressee );
01825 }
01826
01827
01828 QString IncidenceFormatter::formatTNEFInvitation( const QByteArray& tnef,
01829 Calendar *mCalendar, InvitationFormatterHelper *helper )
01830 {
01831 QString vPart = IncidenceFormatter::msTNEFToVPart( tnef );
01832 QString iCal = IncidenceFormatter::formatICalInvitation( vPart, mCalendar, helper );
01833 if( !iCal.isEmpty() )
01834 return iCal;
01835 return vPart;
01836 }
01837
01838
01839
01840
01841
01842
01843
01844
01845 class IncidenceFormatter::ToolTipVisitor : public IncidenceBase::Visitor
01846 {
01847 public:
01848 ToolTipVisitor() : mRichText( true ), mResult( "" ) {}
01849
01850 bool act( IncidenceBase *incidence, bool richText=true)
01851 {
01852 mRichText = richText;
01853 mResult = "";
01854 return incidence ? incidence->accept( *this ) : false;
01855 }
01856 QString result() const { return mResult; }
01857
01858 protected:
01859 bool visit( Event *event );
01860 bool visit( Todo *todo );
01861 bool visit( Journal *journal );
01862 bool visit( FreeBusy *fb );
01863
01864 QString dateRangeText( Event*event );
01865 QString dateRangeText( Todo *todo );
01866 QString dateRangeText( Journal *journal );
01867 QString dateRangeText( FreeBusy *fb );
01868
01869 QString generateToolTip( Incidence* incidence, QString dtRangeText );
01870
01871 protected:
01872 bool mRichText;
01873 QString mResult;
01874 };
01875
01876 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Event*event )
01877 {
01878 QString ret;
01879 QString tmp;
01880 if ( event->isMultiDay() ) {
01881
01882 tmp = "<br>" + i18n("Event start", "<i>From:</i> %1");
01883 if (event->doesFloat())
01884 ret += tmp.arg( event->dtStartDateStr().replace(" ", " ") );
01885 else
01886 ret += tmp.arg( event->dtStartStr().replace(" ", " ") );
01887
01888 tmp = "<br>" + i18n("Event end","<i>To:</i> %1");
01889 if (event->doesFloat())
01890 ret += tmp.arg( event->dtEndDateStr().replace(" ", " ") );
01891 else
01892 ret += tmp.arg( event->dtEndStr().replace(" ", " ") );
01893
01894 } else {
01895
01896 ret += "<br>"+i18n("<i>Date:</i> %1").
01897 arg( event->dtStartDateStr().replace(" ", " ") );
01898 if ( !event->doesFloat() ) {
01899 const QString dtStartTime = event->dtStartTimeStr().replace( " ", " " );
01900 const QString dtEndTime = event->dtEndTimeStr().replace( " ", " " );
01901 if ( dtStartTime == dtEndTime ) {
01902 tmp = "<br>" + i18n("time for event, to prevent ugly line breaks",
01903 "<i>Time:</i> %1").
01904 arg( dtStartTime );
01905 } else {
01906 tmp = "<br>" + i18n("time range for event, to prevent ugly line breaks",
01907 "<i>Time:</i> %1 - %2").
01908 arg( dtStartTime, dtEndTime );
01909 }
01910 ret += tmp;
01911 }
01912
01913 }
01914 return ret;
01915 }
01916
01917 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Todo*todo )
01918 {
01919 QString ret;
01920 bool floats( todo->doesFloat() );
01921 if (todo->hasStartDate())
01922
01923
01924
01925 ret += "<br>" + i18n("<i>Start:</i> %1").arg(
01926 (floats)
01927 ?(todo->dtStartDateStr().replace(" ", " "))
01928 :(todo->dtStartStr().replace(" ", " ")) ) ;
01929 if (todo->hasDueDate())
01930 ret += "<br>" + i18n("<i>Due:</i> %1").arg(
01931 (floats)
01932 ?(todo->dtDueDateStr().replace(" ", " "))
01933 :(todo->dtDueStr().replace(" ", " ")) );
01934 if (todo->isCompleted())
01935 ret += "<br>" + i18n("<i>Completed:</i> %1").arg( todo->completedStr().replace(" ", " ") );
01936 else
01937 ret += "<br>" + i18n("%1 % completed").arg(todo->percentComplete());
01938
01939 return ret;
01940 }
01941
01942 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Journal*journal )
01943 {
01944 QString ret;
01945 if (journal->dtStart().isValid() ) {
01946 ret += "<br>" + i18n("<i>Date:</i> %1").arg( journal->dtStartDateStr( false ) );
01947 }
01948 return ret;
01949 }
01950
01951 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( FreeBusy *fb )
01952 {
01953 QString tmp( "<br>" + i18n("<i>Period start:</i> %1") );
01954 QString ret = tmp.arg( KGlobal::locale()->formatDateTime( fb->dtStart() ) );
01955 tmp = "<br>" + i18n("<i>Period start:</i> %1");
01956 ret += tmp.arg( KGlobal::locale()->formatDateTime( fb->dtEnd() ) );
01957 return ret;
01958 }
01959
01960
01961
01962 bool IncidenceFormatter::ToolTipVisitor::visit( Event *event )
01963 {
01964 mResult = generateToolTip( event, dateRangeText( event ) );
01965 return !mResult.isEmpty();
01966 }
01967
01968 bool IncidenceFormatter::ToolTipVisitor::visit( Todo *todo )
01969 {
01970 mResult = generateToolTip( todo, dateRangeText( todo ) );
01971 return !mResult.isEmpty();
01972 }
01973
01974 bool IncidenceFormatter::ToolTipVisitor::visit( Journal *journal )
01975 {
01976 mResult = generateToolTip( journal, dateRangeText( journal ) );
01977 return !mResult.isEmpty();
01978 }
01979
01980 bool IncidenceFormatter::ToolTipVisitor::visit( FreeBusy *fb )
01981 {
01982 mResult = "<qt><b>" + i18n("Free/Busy information for %1")
01983 .arg(fb->organizer().fullName()) + "</b>";
01984 mResult += dateRangeText( fb );
01985 mResult += "</qt>";
01986 return !mResult.isEmpty();
01987 }
01988
01989 QString IncidenceFormatter::ToolTipVisitor::generateToolTip( Incidence* incidence, QString dtRangeText )
01990 {
01991 if ( !incidence )
01992 return QString::null;
01993
01994 QString tmp = "<qt><b>"+ incidence->summary().replace("\n", "<br>")+"</b>";
01995
01996 tmp += dtRangeText;
01997
01998 if (!incidence->location().isEmpty()) {
01999
02000 tmp += "<br>"+i18n("<i>Location:</i> %1").
02001 arg( incidence->location().replace("\n", "<br>") );
02002 }
02003 if (!incidence->description().isEmpty()) {
02004 QString desc(incidence->description());
02005 if (desc.length()>120) {
02006 desc = desc.left(120) + "...";
02007 }
02008 tmp += "<br>----------<br>" + i18n("<i>Description:</i><br>") + desc.replace("\n", "<br>");
02009 }
02010 tmp += "</qt>";
02011 return tmp;
02012 }
02013
02014 QString IncidenceFormatter::toolTipString( IncidenceBase *incidence, bool richText )
02015 {
02016 ToolTipVisitor v;
02017 if ( v.act( incidence, richText ) ) {
02018 return v.result();
02019 } else
02020 return QString::null;
02021 }
02022
02023
02024
02025
02026
02027
02028
02029
02030 class IncidenceFormatter::MailBodyVisitor : public IncidenceBase::Visitor
02031 {
02032 public:
02033 MailBodyVisitor() : mResult( "" ) {}
02034
02035 bool act( IncidenceBase *incidence )
02036 {
02037 mResult = "";
02038 return incidence ? incidence->accept( *this ) : false;
02039 }
02040 QString result() const { return mResult; }
02041
02042 protected:
02043 bool visit( Event *event );
02044 bool visit( Todo *todo );
02045 bool visit( Journal *journal );
02046 bool visit( FreeBusy * ) { mResult = i18n("This is a Free Busy Object"); return !mResult.isEmpty(); }
02047 protected:
02048 QString mResult;
02049 };
02050
02051
02052 static QString mailBodyIncidence( Incidence *incidence )
02053 {
02054 QString body;
02055 if ( !incidence->summary().isEmpty() ) {
02056 body += i18n("Summary: %1\n").arg( incidence->summary() );
02057 }
02058 if ( !incidence->organizer().isEmpty() ) {
02059 body += i18n("Organizer: %1\n").arg( incidence->organizer().fullName() );
02060 }
02061 if ( !incidence->location().isEmpty() ) {
02062 body += i18n("Location: %1\n").arg( incidence->location() );
02063 }
02064 return body;
02065 }
02066
02067 bool IncidenceFormatter::MailBodyVisitor::visit( Event *event )
02068 {
02069 QString recurrence[]= {i18n("no recurrence", "None"),
02070 i18n("Minutely"), i18n("Hourly"), i18n("Daily"),
02071 i18n("Weekly"), i18n("Monthly Same Day"), i18n("Monthly Same Position"),
02072 i18n("Yearly"), i18n("Yearly"), i18n("Yearly")};
02073
02074 mResult = mailBodyIncidence( event );
02075 mResult += i18n("Start Date: %1\n").arg( event->dtStartDateStr() );
02076 if ( !event->doesFloat() ) {
02077 mResult += i18n("Start Time: %1\n").arg( event->dtStartTimeStr() );
02078 }
02079 if ( event->dtStart() != event->dtEnd() ) {
02080 mResult += i18n("End Date: %1\n").arg( event->dtEndDateStr() );
02081 }
02082 if ( !event->doesFloat() ) {
02083 mResult += i18n("End Time: %1\n").arg( event->dtEndTimeStr() );
02084 }
02085 if ( event->doesRecur() ) {
02086 Recurrence *recur = event->recurrence();
02087
02088 mResult += i18n("Recurs: %1\n")
02089 .arg( recurrence[ recur->recurrenceType() ] );
02090 mResult += i18n("Frequency: %1\n")
02091 .arg( event->recurrence()->frequency() );
02092
02093 if ( recur->duration() > 0 ) {
02094 mResult += i18n ("Repeats once", "Repeats %n times", recur->duration());
02095 mResult += '\n';
02096 } else {
02097 if ( recur->duration() != -1 ) {
02098
02099 QString endstr;
02100 if ( event->doesFloat() ) {
02101 endstr = KGlobal::locale()->formatDate( recur->endDate() );
02102 } else {
02103 endstr = KGlobal::locale()->formatDateTime( recur->endDateTime() );
02104 }
02105 mResult += i18n("Repeat until: %1\n").arg( endstr );
02106 } else {
02107 mResult += i18n("Repeats forever\n");
02108 }
02109 }
02110 }
02111 QString details = event->description();
02112 if ( !details.isEmpty() ) {
02113 mResult += i18n("Details:\n%1\n").arg( details );
02114 }
02115 return !mResult.isEmpty();
02116 }
02117
02118 bool IncidenceFormatter::MailBodyVisitor::visit( Todo *todo )
02119 {
02120 mResult = mailBodyIncidence( todo );
02121
02122 if ( todo->hasStartDate() ) {
02123 mResult += i18n("Start Date: %1\n").arg( todo->dtStartDateStr() );
02124 if ( !todo->doesFloat() ) {
02125 mResult += i18n("Start Time: %1\n").arg( todo->dtStartTimeStr() );
02126 }
02127 }
02128 if ( todo->hasDueDate() ) {
02129 mResult += i18n("Due Date: %1\n").arg( todo->dtDueDateStr() );
02130 if ( !todo->doesFloat() ) {
02131 mResult += i18n("Due Time: %1\n").arg( todo->dtDueTimeStr() );
02132 }
02133 }
02134 QString details = todo->description();
02135 if ( !details.isEmpty() ) {
02136 mResult += i18n("Details:\n%1\n").arg( details );
02137 }
02138 return !mResult.isEmpty();
02139 }
02140
02141 bool IncidenceFormatter::MailBodyVisitor::visit( Journal *journal )
02142 {
02143 mResult = mailBodyIncidence( journal );
02144 mResult += i18n("Date: %1\n").arg( journal->dtStartDateStr() );
02145 if ( !journal->doesFloat() ) {
02146 mResult += i18n("Time: %1\n").arg( journal->dtStartTimeStr() );
02147 }
02148 if ( !journal->description().isEmpty() )
02149 mResult += i18n("Text of the journal:\n%1\n").arg( journal->description() );
02150 return !mResult.isEmpty();
02151 }
02152
02153
02154
02155 QString IncidenceFormatter::mailBodyString( IncidenceBase *incidence )
02156 {
02157 if ( !incidence )
02158 return QString::null;
02159
02160 MailBodyVisitor v;
02161 if ( v.act( incidence ) ) {
02162 return v.result();
02163 }
02164 return QString::null;
02165 }
02166
02167 static QString recurEnd( Incidence *incidence )
02168 {
02169 QString endstr;
02170 if ( incidence->doesFloat() ) {
02171 endstr = KGlobal::locale()->formatDate( incidence->recurrence()->endDate() );
02172 } else {
02173 endstr = KGlobal::locale()->formatDateTime( incidence->recurrence()->endDateTime() );
02174 }
02175 return endstr;
02176 }
02177
02178 QString IncidenceFormatter::recurrenceString(Incidence * incidence)
02179 {
02180 if ( !incidence->doesRecur() )
02181 return i18n( "No recurrence" );
02182
02183 Recurrence *recur = incidence->recurrence();
02184 switch ( recur->recurrenceType() ) {
02185 case Recurrence::rNone:
02186 return i18n( "No recurrence" );
02187 case Recurrence::rMinutely:
02188 if ( recur->duration() != -1 )
02189 return i18n( "Recurs every minute until %1", "Recurs every %n minutes until %1", recur->frequency() )
02190 .arg( recurEnd( incidence ) );
02191 return i18n( "Recurs every minute", "Recurs every %n minutes", recur->frequency() );
02192 case Recurrence::rHourly:
02193 if ( recur->duration() != -1 )
02194 return i18n( "Recurs hourly until %1", "Recurs every %n hours until %1", recur->frequency() )
02195 .arg( recurEnd( incidence ) );
02196 return i18n( "Recurs hourly", "Recurs every %n hours", recur->frequency() );
02197 case Recurrence::rDaily:
02198 if ( recur->duration() != -1 )
02199 return i18n( "Recurs daily until %1", "Recurs every %n days until %1", recur->frequency() )
02200 .arg( recurEnd( incidence ) );
02201 return i18n( "Recurs daily", "Recurs every %n days", recur->frequency() );
02202 case Recurrence::rWeekly:
02203 if ( recur->duration() != -1 )
02204 return i18n( "Recurs weekly until %1", "Recurs every %n weeks until %1", recur->frequency() )
02205 .arg( recurEnd( incidence ) );
02206 return i18n( "Recurs weekly", "Recurs every %n weeks", recur->frequency() );
02207 case Recurrence::rMonthlyPos:
02208 case Recurrence::rMonthlyDay:
02209 if ( recur->duration() != -1 )
02210 return i18n( "Recurs monthly until %1", "Recurs every %n month until %1", recur->frequency() )
02211 .arg( recurEnd( incidence ) );
02212 return i18n( "Recurs monthly", "Recurs every %n month", recur->frequency() );
02213 case Recurrence::rYearlyMonth:
02214 case Recurrence::rYearlyDay:
02215 case Recurrence::rYearlyPos:
02216 if ( recur->duration() != -1 )
02217 return i18n( "Recurs yearly until %1", "Recurs every %n years until %1", recur->frequency() )
02218 .arg( recurEnd( incidence ) );
02219 return i18n( "Recurs yearly", "Recurs every %n years", recur->frequency() );
02220 default:
02221 return i18n( "Incidence recurs" );
02222 }
02223 }