1 | /* |
2 | This file is part of the kcal library. |
3 | |
4 | Copyright (c) 2000,2001 Cornelius Schumacher <schumacher@kde.org> |
5 | Copyright (C) 2004 Reinhold Kainhofer <reinhold@kainhofer.com> |
6 | |
7 | This library is free software; you can redistribute it and/or |
8 | modify it under the terms of the GNU Library General Public |
9 | License as published by the Free Software Foundation; either |
10 | version 2 of the License, or (at your option) any later version. |
11 | |
12 | This library is distributed in the hope that it will be useful, |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | Library General Public License for more details. |
16 | |
17 | You should have received a copy of the GNU Library General Public License |
18 | along with this library; see the file COPYING.LIB. If not, write to |
19 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
20 | Boston, MA 02110-1301, USA. |
21 | */ |
22 | |
23 | #include "htmlexport.h" |
24 | #include "htmlexportsettings.h" |
25 | #include "incidenceformatter.h" |
26 | #include "calendar.h" |
27 | #include "event.h" |
28 | #include "todo.h" |
29 | #ifndef KORG_NOKABC |
30 | #include "kabc/stdaddressbook.h" |
31 | #endif |
32 | |
33 | #include <kglobal.h> |
34 | #include <klocalizedstring.h> |
35 | #include <kdebug.h> |
36 | #include <kcalendarsystem.h> |
37 | |
38 | #include <QtCore/QFile> |
39 | #include <QtCore/QTextStream> |
40 | #include <QtCore/QTextCodec> |
41 | #include <QtCore/QRegExp> |
42 | #include <QtCore/QMap> |
43 | #include <QApplication> |
44 | |
45 | using namespace KCal; |
46 | |
47 | static QString cleanChars( const QString &txt ); |
48 | |
49 | //@cond PRIVATE |
50 | class KCal::HtmlExport::Private |
51 | { |
52 | public: |
53 | Private( Calendar *calendar, HTMLExportSettings *settings ) |
54 | : mCalendar( calendar ), |
55 | mSettings( settings ) |
56 | {} |
57 | |
58 | Calendar *mCalendar; |
59 | HTMLExportSettings *mSettings; |
60 | QMap<QDate,QString> mHolidayMap; |
61 | }; |
62 | //@endcond |
63 | |
64 | HtmlExport::HtmlExport( Calendar *calendar, HTMLExportSettings *settings ) |
65 | : d( new Private( calendar, settings ) ) |
66 | { |
67 | } |
68 | |
69 | HtmlExport::~HtmlExport() |
70 | { |
71 | delete d; |
72 | } |
73 | |
74 | bool HtmlExport::save( const QString &fileName ) |
75 | { |
76 | QString fn( fileName ); |
77 | if ( fn.isEmpty() && d->mSettings ) { |
78 | fn = d->mSettings->outputFile(); |
79 | } |
80 | if ( !d->mSettings || fn.isEmpty() ) { |
81 | return false; |
82 | } |
83 | QFile f( fileName ); |
84 | if ( !f.open( QIODevice::WriteOnly ) ) { |
85 | return false; |
86 | } |
87 | QTextStream ts( &f ); |
88 | bool success = save( &ts ); |
89 | f.close(); |
90 | return success; |
91 | } |
92 | |
93 | bool HtmlExport::save( QTextStream *ts ) |
94 | { |
95 | if ( !d->mSettings ) { |
96 | return false; |
97 | } |
98 | ts->setCodec( "UTF-8" ); |
99 | // Write HTML header |
100 | *ts << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" " ; |
101 | *ts << "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">" << endl; |
102 | |
103 | *ts << "<html><head>" << endl; |
104 | *ts << " <meta http-equiv=\"Content-Type\" content=\"text/html; charset=" ; |
105 | *ts << "UTF-8\" />" << endl; |
106 | if ( !d->mSettings->pageTitle().isEmpty() ) { |
107 | *ts << " <title>" << d->mSettings->pageTitle() << "</title>" << endl; |
108 | } |
109 | *ts << " <style type=\"text/css\">" << endl; |
110 | *ts << styleSheet(); |
111 | *ts << " </style>" << endl; |
112 | *ts << "</head><body>" << endl; |
113 | |
114 | // FIXME: Write header |
115 | // (Heading, Calendar-Owner, Calendar-Date, ...) |
116 | |
117 | if ( d->mSettings->eventView() || d->mSettings->monthView() || d->mSettings->weekView() ) { |
118 | if ( !d->mSettings->eventTitle().isEmpty() ) { |
119 | *ts << "<h1>" << d->mSettings->eventTitle() << "</h1>" << endl; |
120 | } |
121 | |
122 | // Write Week View |
123 | if ( d->mSettings->weekView() ) { |
124 | createWeekView( ts ); |
125 | } |
126 | // Write Month View |
127 | if ( d->mSettings->monthView() ) { |
128 | createMonthView( ts ); |
129 | } |
130 | // Write Event List |
131 | if ( d->mSettings->eventView() ) { |
132 | createEventList( ts ); |
133 | } |
134 | } |
135 | |
136 | // Write Todo List |
137 | if ( d->mSettings->todoView() ) { |
138 | if ( !d->mSettings->todoListTitle().isEmpty() ) { |
139 | *ts << "<h1>" << d->mSettings->todoListTitle() << "</h1>" << endl; |
140 | } |
141 | createTodoList( ts ); |
142 | } |
143 | |
144 | // Write Journals |
145 | if ( d->mSettings->journalView() ) { |
146 | if ( !d->mSettings->journalTitle().isEmpty() ) { |
147 | *ts << "<h1>" << d->mSettings->journalTitle() << "</h1>" << endl; |
148 | } |
149 | createJournalView( ts ); |
150 | } |
151 | |
152 | // Write Free/Busy |
153 | if ( d->mSettings->freeBusyView() ) { |
154 | if ( !d->mSettings->freeBusyTitle().isEmpty() ) { |
155 | *ts << "<h1>" << d->mSettings->freeBusyTitle() << "</h1>" << endl; |
156 | } |
157 | createFreeBusyView( ts ); |
158 | } |
159 | |
160 | createFooter( ts ); |
161 | |
162 | // Write HTML trailer |
163 | *ts << "</body></html>" << endl; |
164 | |
165 | return true; |
166 | } |
167 | |
168 | void HtmlExport::createMonthView( QTextStream *ts ) |
169 | { |
170 | QDate start = fromDate(); |
171 | start.setYMD( start.year(), start.month(), 1 ); // go back to first day in month |
172 | |
173 | QDate end( start.year(), start.month(), start.daysInMonth() ); |
174 | |
175 | int startmonth = start.month(); |
176 | int startyear = start.year(); |
177 | |
178 | while ( start < toDate() ) { |
179 | // Write header |
180 | QDate hDate( start.year(), start.month(), 1 ); |
181 | QString hMon = hDate.toString( "MMMM" ); |
182 | QString hYear = hDate.toString( "yyyy" ); |
183 | *ts << "<h2>" |
184 | << i18nc( "@title month and year" , "%1 %2" , hMon, hYear ) |
185 | << "</h2>" << endl; |
186 | if ( KGlobal::locale()->weekStartDay() == 1 ) { |
187 | start = start.addDays( 1 - start.dayOfWeek() ); |
188 | } else { |
189 | if ( start.dayOfWeek() != 7 ) { |
190 | start = start.addDays( -start.dayOfWeek() ); |
191 | } |
192 | } |
193 | *ts << "<table border=\"1\">" << endl; |
194 | |
195 | // Write table header |
196 | *ts << " <tr>" ; |
197 | for ( int i=0; i < 7; ++i ) { |
198 | *ts << "<th>" << KGlobal::locale()->calendar()->weekDayName( start.addDays(i) ) << "</th>" ; |
199 | } |
200 | *ts << "</tr>" << endl; |
201 | |
202 | // Write days |
203 | while ( start <= end ) { |
204 | *ts << " <tr>" << endl; |
205 | for ( int i=0; i < 7; ++i ) { |
206 | *ts << " <td valign=\"top\"><table border=\"0\">" ; |
207 | |
208 | *ts << "<tr><td " ; |
209 | if ( d->mHolidayMap.contains( start ) || start.dayOfWeek() == 7 ) { |
210 | *ts << "class=\"dateholiday\"" ; |
211 | } else { |
212 | *ts << "class=\"date\"" ; |
213 | } |
214 | *ts << ">" << QString::number( start.day() ); |
215 | |
216 | if ( d->mHolidayMap.contains( start ) ) { |
217 | *ts << " <em>" << d->mHolidayMap[start] << "</em>" ; |
218 | } |
219 | |
220 | *ts << "</td></tr><tr><td valign=\"top\">" ; |
221 | |
222 | // Only print events within the from-to range |
223 | if ( start >= fromDate() && start <= toDate() ) { |
224 | Event::List events = d->mCalendar->events( start, d->mCalendar->timeSpec(), |
225 | EventSortStartDate, |
226 | SortDirectionAscending ); |
227 | if ( events.count() ) { |
228 | *ts << "<table>" ; |
229 | Event::List::ConstIterator it; |
230 | for ( it = events.constBegin(); it != events.constEnd(); ++it ) { |
231 | if ( checkSecrecy( *it ) ) { |
232 | createEvent( ts, *it, start, false ); |
233 | } |
234 | } |
235 | *ts << "</table>" ; |
236 | } else { |
237 | *ts << " " ; |
238 | } |
239 | } |
240 | |
241 | *ts << "</td></tr></table></td>" << endl; |
242 | start = start.addDays( 1 ); |
243 | } |
244 | *ts << " </tr>" << endl; |
245 | } |
246 | *ts << "</table>" << endl; |
247 | startmonth += 1; |
248 | if ( startmonth > 12 ) { |
249 | startyear += 1; |
250 | startmonth = 1; |
251 | } |
252 | start.setYMD( startyear, startmonth, 1 ); |
253 | end.setYMD( start.year(), start.month(), start.daysInMonth() ); |
254 | } |
255 | } |
256 | |
257 | void HtmlExport::createEventList( QTextStream *ts ) |
258 | { |
259 | int columns = 3; |
260 | *ts << "<table border=\"0\" cellpadding=\"3\" cellspacing=\"3\">" << endl; |
261 | *ts << " <tr>" << endl; |
262 | *ts << " <th class=\"sum\">" << i18nc( "@title:column event start time" , |
263 | "Start Time" ) << "</th>" << endl; |
264 | *ts << " <th>" << i18nc( "@title:column event end time" , |
265 | "End Time" ) << "</th>" << endl; |
266 | *ts << " <th>" << i18nc( "@title:column event description" , |
267 | "Event" ) << "</th>" << endl; |
268 | if ( d->mSettings->eventLocation() ) { |
269 | *ts << " <th>" << i18nc( "@title:column event location" , |
270 | "Location" ) << "</th>" << endl; |
271 | ++columns; |
272 | } |
273 | if ( d->mSettings->eventCategories() ) { |
274 | *ts << " <th>" << i18nc( "@title:column event categories" , |
275 | "Categories" ) << "</th>" << endl; |
276 | ++columns; |
277 | } |
278 | if ( d->mSettings->eventAttendees() ) { |
279 | *ts << " <th>" << i18nc( "@title:column event attendees" , |
280 | "Attendees" ) << "</th>" << endl; |
281 | ++columns; |
282 | } |
283 | |
284 | *ts << " </tr>" << endl; |
285 | |
286 | for ( QDate dt = fromDate(); dt <= toDate(); dt = dt.addDays(1) ) { |
287 | kDebug() << "Getting events for" << dt.toString(); |
288 | Event::List events = d->mCalendar->events( dt, d->mCalendar->timeSpec(), |
289 | EventSortStartDate, |
290 | SortDirectionAscending ); |
291 | if ( events.count() ) { |
292 | *ts << " <tr><td colspan=\"" << QString::number( columns ) |
293 | << "\" class=\"datehead\"><i>" |
294 | << KGlobal::locale()->formatDate( dt ) |
295 | << "</i></td></tr>" << endl; |
296 | |
297 | Event::List::ConstIterator it; |
298 | for ( it = events.constBegin(); it != events.constEnd(); ++it ) { |
299 | if ( checkSecrecy( *it ) ) { |
300 | createEvent( ts, *it, dt ); |
301 | } |
302 | } |
303 | } |
304 | } |
305 | |
306 | *ts << "</table>" << endl; |
307 | } |
308 | |
309 | void HtmlExport::createEvent ( QTextStream *ts, Event *event, |
310 | QDate date, bool withDescription ) |
311 | { |
312 | kDebug() << event->summary(); |
313 | *ts << " <tr>" << endl; |
314 | |
315 | if ( !event->allDay() ) { |
316 | if ( event->isMultiDay( d->mCalendar->timeSpec() ) && ( event->dtStart().date() != date ) ) { |
317 | *ts << " <td> </td>" << endl; |
318 | } else { |
319 | *ts << " <td valign=\"top\">" |
320 | << IncidenceFormatter::timeToString( event->dtStart(), true, d->mCalendar->timeSpec() ) |
321 | << "</td>" << endl; |
322 | } |
323 | if ( event->isMultiDay( d->mCalendar->timeSpec() ) && ( event->dtEnd().date() != date ) ) { |
324 | *ts << " <td> </td>" << endl; |
325 | } else { |
326 | *ts << " <td valign=\"top\">" |
327 | << IncidenceFormatter::timeToString( event->dtEnd(), true, d->mCalendar->timeSpec() ) |
328 | << "</td>" << endl; |
329 | } |
330 | } else { |
331 | *ts << " <td> </td><td> </td>" << endl; |
332 | } |
333 | |
334 | *ts << " <td class=\"sum\">" << endl; |
335 | *ts << " <b>" << cleanChars( event->summary() ) << "</b>" << endl; |
336 | if ( withDescription && !event->description().isEmpty() ) { |
337 | *ts << " <p>" << breakString( cleanChars( event->description() ) ) << "</p>" << endl; |
338 | } |
339 | *ts << " </td>" << endl; |
340 | |
341 | if ( d->mSettings->eventLocation() ) { |
342 | *ts << " <td>" << endl; |
343 | formatLocation( ts, event ); |
344 | *ts << " </td>" << endl; |
345 | } |
346 | |
347 | if ( d->mSettings->eventCategories() ) { |
348 | *ts << " <td>" << endl; |
349 | formatCategories( ts, event ); |
350 | *ts << " </td>" << endl; |
351 | } |
352 | |
353 | if ( d->mSettings->eventAttendees() ) { |
354 | *ts << " <td>" << endl; |
355 | formatAttendees( ts, event ); |
356 | *ts << " </td>" << endl; |
357 | } |
358 | |
359 | *ts << " </tr>" << endl; |
360 | } |
361 | |
362 | void HtmlExport::createTodoList ( QTextStream *ts ) |
363 | { |
364 | Todo::List rawTodoList = d->mCalendar->todos(); |
365 | |
366 | int index = 0; |
367 | while ( index < rawTodoList.count() ) { |
368 | Todo *ev = rawTodoList[ index ]; |
369 | Todo *subev = ev; |
370 | if ( ev->relatedTo() ) { |
371 | if ( ev->relatedTo()->type() == "Todo" ) { |
372 | if ( !rawTodoList.contains( static_cast<Todo *>( ev->relatedTo() ) ) ) { |
373 | rawTodoList.append( static_cast<Todo *>( ev->relatedTo() ) ); |
374 | } |
375 | } |
376 | } |
377 | index = rawTodoList.indexOf( subev ); |
378 | ++index; |
379 | } |
380 | |
381 | // FIXME: Sort list by priorities. This is brute force and should be |
382 | // replaced by a real sorting algorithm. |
383 | Todo::List todoList; |
384 | Todo::List::ConstIterator it; |
385 | for ( int i = 1; i <= 9; ++i ) { |
386 | for ( it = rawTodoList.constBegin(); it != rawTodoList.constEnd(); ++it ) { |
387 | if ( (*it)->priority() == i && checkSecrecy( *it ) ) { |
388 | todoList.append( *it ); |
389 | } |
390 | } |
391 | } |
392 | for ( it = rawTodoList.constBegin(); it != rawTodoList.constEnd(); ++it ) { |
393 | if ( (*it)->priority() == 0 && checkSecrecy( *it ) ) { |
394 | todoList.append( *it ); |
395 | } |
396 | } |
397 | |
398 | int columns = 3; |
399 | *ts << "<table border=\"0\" cellpadding=\"3\" cellspacing=\"3\">" << endl; |
400 | *ts << " <tr>" << endl; |
401 | *ts << " <th class=\"sum\">" << i18nc( "@title:column" , "To-do" ) << "</th>" << endl; |
402 | *ts << " <th>" << i18nc( "@title:column to-do priority" , "Priority" ) << "</th>" << endl; |
403 | *ts << " <th>" << i18nc( "@title:column to-do percent completed" , |
404 | "Completed" ) << "</th>" << endl; |
405 | if ( d->mSettings->taskDueDate() ) { |
406 | *ts << " <th>" << i18nc( "@title:column to-do due date" , "Due Date" ) << "</th>" << endl; |
407 | ++columns; |
408 | } |
409 | if ( d->mSettings->taskLocation() ) { |
410 | *ts << " <th>" << i18nc( "@title:column to-do location" , "Location" ) << "</th>" << endl; |
411 | ++columns; |
412 | } |
413 | if ( d->mSettings->taskCategories() ) { |
414 | *ts << " <th>" << i18nc( "@title:column to-do categories" , "Categories" ) << "</th>" << endl; |
415 | ++columns; |
416 | } |
417 | if ( d->mSettings->taskAttendees() ) { |
418 | *ts << " <th>" << i18nc( "@title:column to-do attendees" , "Attendees" ) << "</th>" << endl; |
419 | ++columns; |
420 | } |
421 | *ts << " </tr>" << endl; |
422 | |
423 | // Create top-level list. |
424 | for ( it = todoList.constBegin(); it != todoList.constEnd(); ++it ) { |
425 | if ( !(*it)->relatedTo() ) { |
426 | createTodo( ts, *it ); |
427 | } |
428 | } |
429 | |
430 | // Create sub-level lists |
431 | for ( it = todoList.constBegin(); it != todoList.constEnd(); ++it ) { |
432 | Incidence::List relations = (*it)->relations(); |
433 | if ( relations.count() ) { |
434 | // Generate sub-to-do list |
435 | *ts << " <tr>" << endl; |
436 | *ts << " <td class=\"subhead\" colspan=" ; |
437 | *ts << "\"" << QString::number(columns) << "\"" ; |
438 | *ts << "><a name=\"sub" << (*it)->uid() << "\"></a>" |
439 | << i18nc( "@title:column sub-to-dos of the parent to-do" , |
440 | "Sub-To-dos of: " ) << "<a href=\"#" |
441 | << (*it)->uid() << "\"><b>" << cleanChars( (*it)->summary() ) |
442 | << "</b></a></td>" << endl; |
443 | *ts << " </tr>" << endl; |
444 | |
445 | Todo::List sortedList; |
446 | // FIXME: Sort list by priorities. This is brute force and should be |
447 | // replaced by a real sorting algorithm. |
448 | for ( int i = 1; i <= 9; ++i ) { |
449 | Incidence::List::ConstIterator it2; |
450 | for ( it2 = relations.constBegin(); it2 != relations.constEnd(); ++it2 ) { |
451 | Todo *ev3 = dynamic_cast<Todo *>( *it2 ); |
452 | if ( ev3 && ev3->priority() == i ) { |
453 | sortedList.append( ev3 ); |
454 | } |
455 | } |
456 | } |
457 | Incidence::List::ConstIterator it2; |
458 | for ( it2 = relations.constBegin(); it2 != relations.constEnd(); ++it2 ) { |
459 | Todo *ev3 = dynamic_cast<Todo *>( *it2 ); |
460 | if ( ev3 && ev3->priority() == 0 ) { |
461 | sortedList.append( ev3 ); |
462 | } |
463 | } |
464 | |
465 | Todo::List::ConstIterator it3; |
466 | for ( it3 = sortedList.constBegin(); it3 != sortedList.constEnd(); ++it3 ) { |
467 | createTodo( ts, *it3 ); |
468 | } |
469 | } |
470 | } |
471 | |
472 | *ts << "</table>" << endl; |
473 | } |
474 | |
475 | void HtmlExport::createTodo( QTextStream *ts, Todo *todo ) |
476 | { |
477 | kDebug(); |
478 | |
479 | bool completed = todo->isCompleted(); |
480 | Incidence::List relations = todo->relations(); |
481 | |
482 | *ts << "<tr>" << endl; |
483 | |
484 | *ts << " <td class=\"sum" ; |
485 | if (completed) *ts << "done" ; |
486 | *ts << "\">" << endl; |
487 | *ts << " <a name=\"" << todo->uid() << "\"></a>" << endl; |
488 | *ts << " <b>" << cleanChars( todo->summary() ) << "</b>" << endl; |
489 | if ( !todo->description().isEmpty() ) { |
490 | *ts << " <p>" << breakString( cleanChars( todo->description() ) ) << "</p>" << endl; |
491 | } |
492 | if ( relations.count() ) { |
493 | *ts << " <div align=\"right\"><a href=\"#sub" << todo->uid() |
494 | << "\">" << i18nc( "@title:column sub-to-dos of the parent to-do" , |
495 | "Sub-To-dos" ) << "</a></div>" << endl; |
496 | } |
497 | *ts << " </td>" << endl; |
498 | |
499 | *ts << " <td" ; |
500 | if ( completed ) { |
501 | *ts << " class=\"done\"" ; |
502 | } |
503 | *ts << ">" << endl; |
504 | *ts << " " << todo->priority() << endl; |
505 | *ts << " </td>" << endl; |
506 | |
507 | *ts << " <td" ; |
508 | if ( completed ) { |
509 | *ts << " class=\"done\"" ; |
510 | } |
511 | *ts << ">" << endl; |
512 | *ts << " " << i18nc( "@info/plain to-do percent complete" , |
513 | "%1 %" , todo->percentComplete() ) << endl; |
514 | *ts << " </td>" << endl; |
515 | |
516 | if ( d->mSettings->taskDueDate() ) { |
517 | *ts << " <td" ; |
518 | if ( completed ) { |
519 | *ts << " class=\"done\"" ; |
520 | } |
521 | *ts << ">" << endl; |
522 | if ( todo->hasDueDate() ) { |
523 | *ts << " " << IncidenceFormatter::dateToString( todo->dtDue( true ) ) << endl; |
524 | } else { |
525 | *ts << " " << endl; |
526 | } |
527 | *ts << " </td>" << endl; |
528 | } |
529 | |
530 | if ( d->mSettings->taskLocation() ) { |
531 | *ts << " <td" ; |
532 | if ( completed ) { |
533 | *ts << " class=\"done\"" ; |
534 | } |
535 | *ts << ">" << endl; |
536 | formatLocation( ts, todo ); |
537 | *ts << " </td>" << endl; |
538 | } |
539 | |
540 | if ( d->mSettings->taskCategories() ) { |
541 | *ts << " <td" ; |
542 | if ( completed ) { |
543 | *ts << " class=\"done\"" ; |
544 | } |
545 | *ts << ">" << endl; |
546 | formatCategories( ts, todo ); |
547 | *ts << " </td>" << endl; |
548 | } |
549 | |
550 | if ( d->mSettings->taskAttendees() ) { |
551 | *ts << " <td" ; |
552 | if ( completed ) { |
553 | *ts << " class=\"done\"" ; |
554 | } |
555 | *ts << ">" << endl; |
556 | formatAttendees( ts, todo ); |
557 | *ts << " </td>" << endl; |
558 | } |
559 | |
560 | *ts << "</tr>" << endl; |
561 | } |
562 | |
563 | void HtmlExport::createWeekView( QTextStream *ts ) |
564 | { |
565 | Q_UNUSED( ts ); |
566 | // FIXME: Implement this! |
567 | } |
568 | |
569 | void HtmlExport::createJournalView( QTextStream *ts ) |
570 | { |
571 | Q_UNUSED( ts ); |
572 | // Journal::List rawJournalList = d->mCalendar->journals(); |
573 | // FIXME: Implement this! |
574 | } |
575 | |
576 | void HtmlExport::createFreeBusyView( QTextStream *ts ) |
577 | { |
578 | Q_UNUSED( ts ); |
579 | // FIXME: Implement this! |
580 | } |
581 | |
582 | bool HtmlExport::checkSecrecy( Incidence *incidence ) |
583 | { |
584 | int secrecy = incidence->secrecy(); |
585 | if ( secrecy == Incidence::SecrecyPublic ) { |
586 | return true; |
587 | } |
588 | if ( secrecy == Incidence::SecrecyPrivate && !d->mSettings->excludePrivate() ) { |
589 | return true; |
590 | } |
591 | if ( secrecy == Incidence::SecrecyConfidential && |
592 | !d->mSettings->excludeConfidential() ) { |
593 | return true; |
594 | } |
595 | return false; |
596 | } |
597 | |
598 | void HtmlExport::formatLocation( QTextStream *ts, Incidence *incidence ) |
599 | { |
600 | if ( !incidence->location().isEmpty() ) { |
601 | *ts << " " << cleanChars( incidence->location() ) << endl; |
602 | } else { |
603 | *ts << " " << endl; |
604 | } |
605 | } |
606 | |
607 | void HtmlExport::formatCategories( QTextStream *ts, Incidence *incidence ) |
608 | { |
609 | if ( !incidence->categoriesStr().isEmpty() ) { |
610 | *ts << " " << cleanChars( incidence->categoriesStr() ) << endl; |
611 | } else { |
612 | *ts << " " << endl; |
613 | } |
614 | } |
615 | |
616 | void HtmlExport::formatAttendees( QTextStream *ts, Incidence *incidence ) |
617 | { |
618 | Attendee::List attendees = incidence->attendees(); |
619 | if ( attendees.count() ) { |
620 | *ts << "<em>" ; |
621 | #if !defined(KORG_NOKABC) && !defined(KDEPIM_NO_KRESOURCES) |
622 | KABC::AddressBook *add_book = KABC::StdAddressBook::self( true ); |
623 | KABC::Addressee::List addressList; |
624 | addressList = add_book->findByEmail( incidence->organizer().email() ); |
625 | if ( !addressList.isEmpty() ) { |
626 | KABC::Addressee o = addressList.first(); |
627 | if ( !o.isEmpty() && addressList.size() < 2 ) { |
628 | *ts << "<a href=\"mailto:" << incidence->organizer().email() << "\">" ; |
629 | *ts << cleanChars( o.formattedName() ) << "</a>" << endl; |
630 | } else { |
631 | *ts << incidence->organizer().fullName(); |
632 | } |
633 | } |
634 | #else |
635 | *ts << incidence->organizer().fullName(); |
636 | #endif |
637 | *ts << "</em><br />" ; |
638 | Attendee::List::ConstIterator it; |
639 | for ( it = attendees.constBegin(); it != attendees.constEnd(); ++it ) { |
640 | Attendee *a = *it; |
641 | if ( !a->email().isEmpty() ) { |
642 | *ts << "<a href=\"mailto:" << a->email(); |
643 | *ts << "\">" << cleanChars( a->name() ) << "</a>" ; |
644 | } else { |
645 | *ts << " " << cleanChars( a->name() ); |
646 | } |
647 | *ts << "<br />" << endl; |
648 | } |
649 | } else { |
650 | *ts << " " << endl; |
651 | } |
652 | } |
653 | |
654 | QString HtmlExport::breakString( const QString &text ) |
655 | { |
656 | int number = text.count( "\n" ); |
657 | if ( number <= 0 ) { |
658 | return text; |
659 | } else { |
660 | QString out; |
661 | QString tmpText = text; |
662 | int pos = 0; |
663 | QString tmp; |
664 | for ( int i = 0; i <= number; ++i ) { |
665 | pos = tmpText.indexOf( "\n" ); |
666 | tmp = tmpText.left( pos ); |
667 | tmpText = tmpText.right( tmpText.length() - pos - 1 ); |
668 | out += tmp + "<br />" ; |
669 | } |
670 | return out; |
671 | } |
672 | } |
673 | |
674 | void HtmlExport::( QTextStream *ts ) |
675 | { |
676 | // FIXME: Implement this in a translatable way! |
677 | QString trailer = i18nc( "@info/plain" , "This page was created " ); |
678 | |
679 | /* bool hasPerson = false; |
680 | bool hasCredit = false; |
681 | bool hasCreditURL = false; |
682 | QString mail, name, credit, creditURL;*/ |
683 | if ( !d->mSettings->eMail().isEmpty() ) { |
684 | if ( !d->mSettings->name().isEmpty() ) { |
685 | trailer += i18nc( "@info/plain page creator email link with name" , |
686 | "by <link url='mailto:%1'>%2</link> " , |
687 | d->mSettings->eMail(), d->mSettings->name() ); |
688 | } else { |
689 | trailer += i18nc( "@info/plain page creator email link" , |
690 | "by <link url='mailto:%1'>%2</link> " , |
691 | d->mSettings->eMail(), d->mSettings->eMail() ); |
692 | } |
693 | } else { |
694 | if ( !d->mSettings->name().isEmpty() ) { |
695 | trailer += i18nc( "@info/plain page creator name only" , |
696 | "by %1 " , d->mSettings->name() ); |
697 | } |
698 | } |
699 | if ( !d->mSettings->creditName().isEmpty() ) { |
700 | if ( !d->mSettings->creditURL().isEmpty() ) { |
701 | trailer += i18nc( "@info/plain page credit with name and link" , |
702 | "with <link url='%1'>%2</link>" , |
703 | d->mSettings->creditURL(), d->mSettings->creditName() ); |
704 | } else { |
705 | trailer += i18nc( "@info/plain page credit name only" , |
706 | "with %1" , d->mSettings->creditName() ); |
707 | } |
708 | } |
709 | *ts << "<p>" << trailer << "</p>" << endl; |
710 | } |
711 | |
712 | QString cleanChars( const QString &text ) |
713 | { |
714 | QString txt = text; |
715 | txt = txt.replace( '&', "&" ); |
716 | txt = txt.replace( '<', "<" ); |
717 | txt = txt.replace( '>', ">" ); |
718 | txt = txt.replace( '\"', """ ); |
719 | txt = txt.replace( QString::fromUtf8( "ä" ), "ä" ); |
720 | txt = txt.replace( QString::fromUtf8( "Ä" ), "Ä" ); |
721 | txt = txt.replace( QString::fromUtf8( "ö" ), "ö" ); |
722 | txt = txt.replace( QString::fromUtf8( "Ö" ), "Ö" ); |
723 | txt = txt.replace( QString::fromUtf8( "ü" ), "ü" ); |
724 | txt = txt.replace( QString::fromUtf8( "Ü" ), "Ü" ); |
725 | txt = txt.replace( QString::fromUtf8( "ß" ), "ß" ); |
726 | txt = txt.replace( QString::fromUtf8( "€" ), "€" ); |
727 | txt = txt.replace( QString::fromUtf8( "é" ), "é" ); |
728 | |
729 | return txt; |
730 | } |
731 | |
732 | QString HtmlExport::styleSheet() const |
733 | { |
734 | if ( !d->mSettings->styleSheet().isEmpty() ) { |
735 | return d->mSettings->styleSheet(); |
736 | } |
737 | |
738 | QString css; |
739 | |
740 | if ( QApplication::isRightToLeft() ) { |
741 | css += " body { background-color:white; color:black; direction: rtl }\n" ; |
742 | css += " td { text-align:center; background-color:#eee }\n" ; |
743 | css += " th { text-align:center; background-color:#228; color:white }\n" ; |
744 | css += " td.sumdone { background-color:#ccc }\n" ; |
745 | css += " td.done { background-color:#ccc }\n" ; |
746 | css += " td.subhead { text-align:center; background-color:#ccf }\n" ; |
747 | css += " td.datehead { text-align:center; background-color:#ccf }\n" ; |
748 | css += " td.space { background-color:white }\n" ; |
749 | css += " td.dateholiday { color:red }\n" ; |
750 | } else { |
751 | css += " body { background-color:white; color:black }\n" ; |
752 | css += " td { text-align:center; background-color:#eee }\n" ; |
753 | css += " th { text-align:center; background-color:#228; color:white }\n" ; |
754 | css += " td.sum { text-align:left }\n" ; |
755 | css += " td.sumdone { text-align:left; background-color:#ccc }\n" ; |
756 | css += " td.done { background-color:#ccc }\n" ; |
757 | css += " td.subhead { text-align:center; background-color:#ccf }\n" ; |
758 | css += " td.datehead { text-align:center; background-color:#ccf }\n" ; |
759 | css += " td.space { background-color:white }\n" ; |
760 | css += " td.date { text-align:left }\n" ; |
761 | css += " td.dateholiday { text-align:left; color:red }\n" ; |
762 | } |
763 | |
764 | return css; |
765 | } |
766 | |
767 | void HtmlExport::addHoliday( const QDate &date, const QString &name ) |
768 | { |
769 | if ( d->mHolidayMap[date].isEmpty() ) { |
770 | d->mHolidayMap[date] = name; |
771 | } else { |
772 | d->mHolidayMap[date] = i18nc( "@info/plain holiday by date and name" , |
773 | "%1, %2" , d->mHolidayMap[date], name ); |
774 | } |
775 | } |
776 | |
777 | QDate HtmlExport::fromDate() const |
778 | { |
779 | return d->mSettings->dateStart().date(); |
780 | } |
781 | |
782 | QDate HtmlExport::toDate() const |
783 | { |
784 | return d->mSettings->dateEnd().date(); |
785 | } |
786 | |