00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 #include <qapplication.h>
00026 #include <qfile.h>
00027 #include <qtextstream.h>
00028 #include <qtextcodec.h>
00029 #include <qregexp.h>
00030
00031 #include <kglobal.h>
00032 #include <klocale.h>
00033 #include <kdebug.h>
00034 #include <kcalendarsystem.h>
00035
00036 #include <libkcal/calendar.h>
00037 #include <libkcal/event.h>
00038 #include <libkcal/todo.h>
00039
00040 #ifndef KORG_NOKABC
00041 #include <kabc/stdaddressbook.h>
00042 #endif
00043 #include "htmlexport.h"
00044
00045 using namespace KCal;
00046
00047 HtmlExport::HtmlExport( Calendar *calendar ) :
00048 mCalendar( calendar ),
00049 mMonthViewEnabled( true ), mEventsEnabled( false ), mTodosEnabled( true ),
00050 mCategoriesTodoEnabled( false ), mAttendeesTodoEnabled( false ),
00051 mCategoriesEventEnabled( false ), mAttendeesEventEnabled( false ),
00052 mDueDateEnabled( false ),
00053 mExcludePrivateTodoEnabled( false ),
00054 mExcludeConfidentialTodoEnabled( false ),
00055 mExcludePrivateEventEnabled( false ),
00056 mExcludeConfidentialEventEnabled( false )
00057 {
00058 mTitle = i18n("KOrganizer Calendar");
00059 mTitleTodo = i18n("KOrganizer To-Do List");
00060 mCreditName = "KOrganizer";
00061 mCreditURL = "http://korganizer.kde.org";
00062 }
00063
00064 bool HtmlExport::save(const QString &fileName)
00065 {
00066 QFile f(fileName);
00067 if (!f.open(IO_WriteOnly)) {
00068 return false;
00069 }
00070 QTextStream ts(&f);
00071 bool success = save(&ts);
00072 f.close();
00073 return success;
00074 }
00075
00076 bool HtmlExport::save(QTextStream *ts)
00077 {
00078 ts->setEncoding(QTextStream::UnicodeUTF8);
00079
00080
00081 *ts << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" ";
00082 *ts << "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
00083
00084 *ts << "<html><head>" << endl;
00085 *ts << " <meta http-equiv=\"Content-Type\" content=\"text/html; charset=";
00086 *ts << "UTF-8\" />\n";
00087 if (!mTitle.isEmpty())
00088 *ts << " <title>" << mTitle << "</title>\n";
00089 *ts << " <style type=\"text/css\">\n";
00090 *ts << styleSheet();
00091 *ts << " </style>\n";
00092 *ts << "</head><body>\n";
00093
00094
00095
00096
00097 if (eventsEnabled() || monthViewEnabled()) {
00098 if (!mTitle.isEmpty())
00099 *ts << "<h1>" << mTitle << "</h1>\n";
00100 }
00101
00102
00103 if (monthViewEnabled()) {
00104 createHtmlMonthView(ts);
00105 }
00106
00107
00108 if (eventsEnabled()) {
00109
00110 createHtmlEventList(ts);
00111 }
00112
00113
00114 if (todosEnabled()) {
00115 if (!mTitleTodo.isEmpty())
00116 *ts << "<h1>" << mTitleTodo << "</h1>\n";
00117
00118
00119 createHtmlTodoList(ts);
00120 }
00121
00122
00123 QString trailer = i18n("This page was created ");
00124
00125 if (!mEmail.isEmpty()) {
00126 if (!mName.isEmpty())
00127 trailer += i18n("by <a href=\"mailto:%1\">%2</a> ").arg( mEmail ).arg( mName );
00128 else
00129 trailer += i18n("by <a href=\"mailto:%1\">%2</a> ").arg( mEmail ).arg( mEmail );
00130 } else {
00131 if (!mName.isEmpty())
00132 trailer += i18n("by %1 ").arg( mName );
00133 }
00134 if (!mCreditName.isEmpty()) {
00135 if (!mCreditURL.isEmpty())
00136 trailer += i18n("with <a href=\"%1\">%2</a>").arg( mCreditURL ).arg( mCreditName );
00137 else
00138 trailer += i18n("with %1").arg( mCreditName );
00139 }
00140 *ts << "<p>" << trailer << "</p>\n";
00141
00142
00143 *ts << "</body></html>\n";
00144
00145 return true;
00146 }
00147
00148 void HtmlExport::createHtmlMonthView(QTextStream *ts)
00149 {
00150 QDate start = fromDate();
00151 start.setYMD(start.year(),start.month(),1);
00152
00153 QDate end(start.year(),start.month(),start.daysInMonth());
00154
00155 int startmonth = start.month();
00156 int startyear = start.year();
00157
00158 while ( start < toDate() ) {
00159
00160 *ts << "<h2>" << (i18n("month_year","%1 %2").arg(KGlobal::locale()->calendar()->monthName(start))
00161 .arg(start.year())) << "</h2>\n";
00162 if ( KGlobal::locale()->weekStartDay() == 1 ) {
00163 start = start.addDays(1 - start.dayOfWeek());
00164 } else {
00165 if (start.dayOfWeek() != 7) {
00166 start = start.addDays(-start.dayOfWeek());
00167 }
00168 }
00169 *ts << "<table border=\"1\">\n";
00170
00171
00172 *ts << " <tr>";
00173 for(int i=0; i<7; ++i) {
00174 *ts << "<th>" << KGlobal::locale()->calendar()->weekDayName( start.addDays(i) ) << "</th>";
00175 }
00176 *ts << "</tr>\n";
00177
00178
00179 while (start <= end) {
00180 *ts << " <tr>\n";
00181 for(int i=0;i<7;++i) {
00182 *ts << " <td valign=\"top\"><table border=\"0\">";
00183
00184 *ts << "<tr><td ";
00185 if (mHolidayMap.contains(start) || start.dayOfWeek() == 7) {
00186 *ts << "class=\"dateholiday\"";
00187 } else {
00188 *ts << "class=\"date\"";
00189 }
00190 *ts << ">" << QString::number(start.day());
00191
00192 if (mHolidayMap.contains(start)) {
00193 *ts << " <em>" << mHolidayMap[start] << "</em>";
00194 }
00195
00196 *ts << "</td></tr><tr><td valign=\"top\">";
00197
00198 Event::List events = mCalendar->events(start,true);
00199 if (events.count()) {
00200 *ts << "<table>";
00201 Event::List::ConstIterator it;
00202 for( it = events.begin(); it != events.end(); ++it ) {
00203 if ( checkSecrecy( *it ) ) {
00204 createHtmlEvent( ts, *it, start, false );
00205 }
00206 }
00207 *ts << "</table>";
00208 } else {
00209 *ts << " ";
00210 }
00211
00212 *ts << "</td></tr></table></td>\n";
00213 start = start.addDays(1);
00214 }
00215 *ts << " </tr>\n";
00216 }
00217 *ts << "</table>\n";
00218 startmonth += 1;
00219 if ( startmonth > 12 ) {
00220 startyear += 1;
00221 startmonth = 1;
00222 }
00223 start.setYMD( startyear, startmonth, 1 );
00224 end.setYMD(start.year(),start.month(),start.daysInMonth());
00225 }
00226 }
00227
00228 void HtmlExport::createHtmlEventList (QTextStream *ts)
00229 {
00230 *ts << "<table border=\"0\" cellpadding=\"3\" cellspacing=\"3\">\n";
00231 *ts << " <tr>\n";
00232 *ts << " <th class=\"sum\">" << i18n("Start Time") << "</th>\n";
00233 *ts << " <th>" << i18n("End Time") << "</th>\n";
00234 *ts << " <th>" << i18n("Event") << "</th>\n";
00235 if (categoriesEventEnabled()) {
00236 *ts << " <th>" << i18n("Categories") << "</th>\n";
00237 }
00238 if (attendeesEventEnabled()) {
00239 *ts << " <th>" << i18n("Attendees") << "</th>\n";
00240 }
00241
00242 *ts << " </tr>\n";
00243
00244 int columns = 3;
00245 if (categoriesEventEnabled()) ++columns;
00246 if (attendeesEventEnabled()) ++columns;
00247
00248 for (QDate dt = fromDate(); dt <= toDate(); dt = dt.addDays(1)) {
00249 kdDebug(5850) << "Getting events for " << dt.toString() << endl;
00250 Event::List events = mCalendar->events(dt,true);
00251 if (events.count()) {
00252 *ts << " <tr><td colspan=\"" << QString::number(columns)
00253 << "\" class=\"datehead\"><i>"
00254 << KGlobal::locale()->formatDate(dt)
00255 << "</i></td></tr>\n";
00256
00257 Event::List::ConstIterator it;
00258 for( it = events.begin(); it != events.end(); ++it ) {
00259 if ( checkSecrecy( *it ) ) {
00260 createHtmlEvent( ts, *it, dt );
00261 }
00262 }
00263 }
00264 }
00265
00266 *ts << "</table>\n";
00267 }
00268
00269 void HtmlExport::createHtmlEvent (QTextStream *ts, Event *event,
00270 QDate date,bool withDescription)
00271 {
00272 kdDebug(5850) << "HtmlExport::createHtmlEvent(): " << event->summary() << endl;
00273 *ts << " <tr>\n";
00274
00275 if (!event->doesFloat()) {
00276 if (event->isMultiDay() && (event->dtStart().date() != date)) {
00277 *ts << " <td> </td>\n";
00278 } else {
00279 *ts << " <td valign=\"top\">" << event->dtStartTimeStr() << "</td>\n";
00280 }
00281 if (event->isMultiDay() && (event->dtEnd().date() != date)) {
00282 *ts << " <td> </td>\n";
00283 } else {
00284 *ts << " <td valign=\"top\">" << event->dtEndTimeStr() << "</td>\n";
00285 }
00286 } else {
00287 *ts << " <td> </td><td> </td>\n";
00288 }
00289
00290 *ts << " <td class=\"sum\">\n";
00291 *ts << " <b>" << cleanChars(event->summary()) << "</b>\n";
00292 if (withDescription && !event->description().isEmpty()) {
00293 *ts << " <p>" << breakString(cleanChars(event->description())) << "</p>\n";
00294 }
00295 *ts << " </td>\n";
00296
00297 if (categoriesEventEnabled()) {
00298 *ts << " <td>\n";
00299 formatHtmlCategories(ts,event);
00300 *ts << " </td>\n";
00301 }
00302
00303 if (attendeesEventEnabled()) {
00304 *ts << " <td>\n";
00305 formatHtmlAttendees(ts,event);
00306 *ts << " </td>\n";
00307 }
00308
00309 *ts << " </tr>\n";
00310 }
00311
00312 void HtmlExport::createHtmlTodoList ( QTextStream *ts )
00313 {
00314 Todo::List rawTodoList = mCalendar->todos();
00315
00316 Todo::List::Iterator it = rawTodoList.begin();
00317 while ( it != rawTodoList.end() ) {
00318 Todo *ev = *it;
00319 Todo *subev = ev;
00320 if ( ev->relatedTo() ) {
00321 if ( ev->relatedTo()->type()=="Todo" ) {
00322 if ( rawTodoList.find( static_cast<Todo *>( ev->relatedTo() ) ) ==
00323 rawTodoList.end() ) {
00324 rawTodoList.append( static_cast<Todo *>( ev->relatedTo() ) );
00325 }
00326 }
00327 }
00328 it = rawTodoList.find( subev );
00329 ++it;
00330 }
00331
00332
00333
00334 Todo::List todoList;
00335 for ( int i = 1; i <= 5; ++i ) {
00336 for( it = rawTodoList.begin(); it != rawTodoList.end(); ++it ) {
00337 if ( (*it)->priority() == i && checkSecrecy( *it ) ) {
00338 todoList.append( *it );
00339 }
00340 }
00341 }
00342
00343 *ts << "<table border=\"0\" cellpadding=\"3\" cellspacing=\"3\">\n";
00344 *ts << " <tr>\n";
00345 *ts << " <th class=\"sum\">" << i18n("Task") << "</th>\n";
00346 *ts << " <th>" << i18n("Priority") << "</th>\n";
00347 *ts << " <th>" << i18n("Completed") << "</th>\n";
00348 if (dueDateEnabled()) {
00349 *ts << " <th>" << i18n("Due Date") << "</th>\n";
00350 }
00351 if (categoriesTodoEnabled()) {
00352 *ts << " <th>" << i18n("Categories") << "</th>\n";
00353 }
00354 if (attendeesTodoEnabled()) {
00355 *ts << " <th>" << i18n("Attendees") << "</th>\n";
00356 }
00357 *ts << " </tr>\n";
00358
00359
00360 for( it = todoList.begin(); it != todoList.end(); ++it ) {
00361 if ( !(*it)->relatedTo() ) createHtmlTodo( ts, *it );
00362 }
00363
00364
00365 for( it = todoList.begin(); it != todoList.end(); ++it ) {
00366 Incidence::List relations = (*it)->relations();
00367 if (relations.count()) {
00368
00369 *ts << " <tr>\n";
00370 *ts << " <td class=\"subhead\" colspan=";
00371 int columns = 3;
00372 if (dueDateEnabled()) ++columns;
00373 if (categoriesTodoEnabled()) ++columns;
00374 if (attendeesTodoEnabled()) ++columns;
00375 *ts << "\"" << QString::number(columns) << "\"";
00376 *ts << "><a name=\"sub" << (*it)->uid() << "\"></a>"
00377 << i18n("Sub-Tasks of: ") << "<a href=\"#"
00378 << (*it)->uid() << "\"><b>" << cleanChars( (*it)->summary())
00379 << "</b></a></td>\n";
00380 *ts << " </tr>\n";
00381
00382 Todo::List sortedList;
00383
00384
00385 for ( int i = 1; i <= 5; ++i ) {
00386 Incidence::List::ConstIterator it2;
00387 for( it2 = relations.begin(); it2 != relations.end(); ++it2 ) {
00388 Todo *ev3 = dynamic_cast<Todo *>( *it2 );
00389 if ( ev3 && ev3->priority() == i ) sortedList.append( ev3 );
00390 }
00391 }
00392
00393 Todo::List::ConstIterator it3;
00394 for( it3 = sortedList.begin(); it3 != sortedList.end(); ++it3 ) {
00395 createHtmlTodo( ts, *it3 );
00396 }
00397 }
00398 }
00399
00400 *ts << "</table>\n";
00401 }
00402
00403 void HtmlExport::createHtmlTodo (QTextStream *ts,Todo *todo)
00404 {
00405 kdDebug(5850) << "HtmlExport::createHtmlTodo()" << endl;
00406
00407 bool completed = todo->isCompleted();
00408 Incidence::List relations = todo->relations();
00409
00410 *ts << "<tr>\n";
00411
00412 *ts << " <td class=\"sum\"";
00413 if (completed) *ts << "done";
00414 *ts << ">\n";
00415 *ts << " <a name=\"" << todo->uid() << "\"></a>\n";
00416 *ts << " <b>" << cleanChars(todo->summary()) << "</b>\n";
00417 if (!todo->description().isEmpty()) {
00418 *ts << " <p>" << breakString(cleanChars(todo->description())) << "</p>\n";
00419 }
00420 if (relations.count()) {
00421 *ts << " <div align=\"right\"><a href=\"#sub" << todo->uid()
00422 << "\">" << i18n("Sub-Tasks") << "</a></div>\n";
00423 }
00424
00425 *ts << " </td";
00426 if (completed) *ts << " class=\"done\"";
00427 *ts << ">\n";
00428
00429 *ts << " <td";
00430 if (completed) *ts << " class=\"done\"";
00431 *ts << ">\n";
00432 *ts << " " << todo->priority() << "\n";
00433 *ts << " </td>\n";
00434
00435 *ts << " <td";
00436 if (completed) *ts << " class=\"done\"";
00437 *ts << ">\n";
00438 *ts << " " << i18n("%1 %").arg(todo->percentComplete()) << "\n";
00439 *ts << " </td>\n";
00440
00441 if (dueDateEnabled()) {
00442 *ts << " <td";
00443 if (completed) *ts << " class=\"done\"";
00444 *ts << ">\n";
00445 if (todo->hasDueDate()) {
00446 *ts << " " << todo->dtDueDateStr() << "\n";
00447 } else {
00448 *ts << " \n";
00449 }
00450 *ts << " </td>\n";
00451 }
00452
00453 if (categoriesTodoEnabled()) {
00454 *ts << " <td";
00455 if (completed) *ts << " class=\"done\"";
00456 *ts << ">\n";
00457 formatHtmlCategories(ts,todo);
00458 *ts << " </td>\n";
00459 }
00460
00461 if (attendeesTodoEnabled()) {
00462 *ts << " <td";
00463 if (completed) *ts << " class=\"done\"";
00464 *ts << ">\n";
00465 formatHtmlAttendees(ts,todo);
00466 *ts << " </td>\n";
00467 }
00468
00469 *ts << "</tr>\n";
00470 }
00471
00472 bool HtmlExport::checkSecrecy( Incidence *incidence )
00473 {
00474 int secrecy = incidence->secrecy();
00475 if ( secrecy == Incidence::SecrecyPublic ) {
00476 return true;
00477 }
00478 if ( secrecy == Incidence::SecrecyPrivate && !excludePrivateEventEnabled() ) {
00479 return true;
00480 }
00481 if ( secrecy == Incidence::SecrecyConfidential &&
00482 !excludeConfidentialEventEnabled() ) {
00483 return true;
00484 }
00485 return false;
00486 }
00487
00488 void HtmlExport::formatHtmlCategories (QTextStream *ts,Incidence *event)
00489 {
00490 if (!event->categoriesStr().isEmpty()) {
00491 *ts << " " << cleanChars(event->categoriesStr()) << "\n";
00492 } else {
00493 *ts << " \n";
00494 }
00495 }
00496
00497 void HtmlExport::formatHtmlAttendees (QTextStream *ts,Incidence *event)
00498 {
00499 Attendee::List attendees = event->attendees();
00500 if (attendees.count()) {
00501 *ts << "<em>";
00502 #ifndef KORG_NOKABC
00503 KABC::AddressBook *add_book = KABC::StdAddressBook::self();
00504 KABC::Addressee::List addressList;
00505 addressList = add_book->findByEmail(event->organizer());
00506 KABC::Addressee o = addressList.first();
00507 if (!o.isEmpty() && addressList.size()<2) {
00508 *ts << "<a href=\"mailto:" << event->organizer() << "\">";
00509 *ts << cleanChars(o.formattedName()) << "</a>\n";
00510 }
00511 else *ts << event->organizer();
00512 #else
00513 *ts << event->organizer();
00514 #endif
00515 *ts << "</em><br />";
00516 Attendee::List::ConstIterator it;
00517 for( it = attendees.begin(); it != attendees.end(); ++it ) {
00518 Attendee *a = *it;
00519 if (!a->email().isEmpty()) {
00520 *ts << "<a href=\"mailto:" << a->email();
00521 *ts << "\">" << cleanChars(a->name()) << "</a>";
00522 }
00523 else {
00524 *ts << " " << cleanChars(a->name());
00525 }
00526 *ts << "<br />" << "\n";
00527 }
00528 } else {
00529 *ts << " \n";
00530 }
00531 }
00532
00533 QString HtmlExport::breakString(const QString &text)
00534 {
00535 int number = text.contains("\n");
00536 if(number < 0) {
00537 return text;
00538 } else {
00539 QString out;
00540 QString tmpText = text;
00541 int pos = 0;
00542 QString tmp;
00543 for(int i=0;i<=number;i++) {
00544 pos = tmpText.find("\n");
00545 tmp = tmpText.left(pos);
00546 tmpText = tmpText.right(tmpText.length() - pos - 1);
00547 out += tmp + "<br />";
00548 }
00549 return out;
00550 }
00551 }
00552
00553 QString HtmlExport::cleanChars(const QString &text)
00554 {
00555 QString txt = text;
00556 txt = txt.replace( "&", "&" );
00557 txt = txt.replace( "<", "<" );
00558 txt = txt.replace( ">", ">" );
00559 txt = txt.replace( "\"", """ );
00560 txt = txt.replace( "ä", "ä" );
00561 txt = txt.replace( "Ä", "Ä" );
00562 txt = txt.replace( "ö", "ö" );
00563 txt = txt.replace( "Ö", "Ö" );
00564 txt = txt.replace( "ü", "ü" );
00565 txt = txt.replace( "Ü", "Ü" );
00566 txt = txt.replace( "ß", "ß" );
00567 txt = txt.replace( "¤", "€" );
00568 txt = txt.replace( "é", "é" );
00569
00570 return txt;
00571 }
00572
00573 void HtmlExport::setStyleSheet( const QString &styleSheet )
00574 {
00575 mStyleSheet = styleSheet;
00576 }
00577
00578 QString HtmlExport::styleSheet()
00579 {
00580 if ( !mStyleSheet.isEmpty() ) return mStyleSheet;
00581
00582 QString css;
00583
00584 if ( QApplication::reverseLayout() ) {
00585 css += " body { background-color:white; color:black; direction: rtl }\n";
00586 css += " td { text-align:center; background-color:#eee }\n";
00587 css += " th { text-align:center; background-color:#228; color:white }\n";
00588 css += " td.sumdone { background-color:#ccc }\n";
00589 css += " td.done { background-color:#ccc }\n";
00590 css += " td.subhead { text-align:center; background-color:#ccf }\n";
00591 css += " td.datehead { text-align:center; background-color:#ccf }\n";
00592 css += " td.space { background-color:white }\n";
00593 css += " td.dateholiday { color:red }\n";
00594 } else {
00595 css += " body { background-color:white; color:black }\n";
00596 css += " td { text-align:center; background-color:#eee }\n";
00597 css += " th { text-align:center; background-color:#228; color:white }\n";
00598 css += " td.sum { text-align:left }\n";
00599 css += " td.sumdone { text-align:left; background-color:#ccc }\n";
00600 css += " td.done { background-color:#ccc }\n";
00601 css += " td.subhead { text-align:center; background-color:#ccf }\n";
00602 css += " td.datehead { text-align:center; background-color:#ccf }\n";
00603 css += " td.space { background-color:white }\n";
00604 css += " td.date { text-align:left }\n";
00605 css += " td.dateholiday { text-align:left; color:red }\n";
00606 }
00607
00608 return css;
00609 }
00610
00611
00612 void HtmlExport::addHoliday( QDate date, QString name)
00613 {
00614 mHolidayMap[date] = name;
00615 }
00616