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