22 #include "htmlexport.h"
23 #include "htmlexportsettings.h"
27 using namespace KCalCore;
30 #include <KCalendarSystem>
32 #include <KLocalizedString>
34 #include <QtCore/QFile>
35 #include <QtCore/QMap>
36 #include <QtCore/QTextStream>
37 #include <QApplication>
39 using namespace KCalUtils;
44 class KCalUtils::HtmlExport::Private
48 : mCalendar(calendar), mSettings(settings)
52 HTMLExportSettings *mSettings;
57 HtmlExport::HtmlExport(
MemoryCalendar *calendar, HTMLExportSettings *settings)
58 : d(new Private(calendar, settings))
62 HtmlExport::~HtmlExport()
70 if (fn.
isEmpty() && d->mSettings) {
71 fn = d->mSettings->outputFile();
73 if (!d->mSettings || fn.
isEmpty()) {
77 if (!f.
open(QIODevice::WriteOnly)) {
81 bool success =
save(&ts);
93 *ts <<
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" ";
94 *ts <<
"\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">" << endl;
96 *ts <<
"<html><head>" << endl;
97 *ts <<
" <meta http-equiv=\"Content-Type\" content=\"text/html; charset=";
98 *ts <<
"UTF-8\" />" << endl;
99 if (!d->mSettings->pageTitle().isEmpty()) {
100 *ts <<
" <title>" << d->mSettings->pageTitle() <<
"</title>" << endl;
102 *ts <<
" <style type=\"text/css\">" << endl;
104 *ts <<
" </style>" << endl;
105 *ts <<
"</head><body>" << endl;
110 if (d->mSettings->eventView() || d->mSettings->monthView() || d->mSettings->weekView()) {
111 if (!d->mSettings->eventTitle().isEmpty()) {
112 *ts <<
"<h1>" << d->mSettings->eventTitle() <<
"</h1>" << endl;
116 if (d->mSettings->weekView()) {
120 if (d->mSettings->monthView()) {
124 if (d->mSettings->eventView()) {
130 if (d->mSettings->todoView()) {
131 if (!d->mSettings->todoListTitle().isEmpty()) {
132 *ts <<
"<h1>" << d->mSettings->todoListTitle() <<
"</h1>" << endl;
138 if (d->mSettings->journalView()) {
139 if (!d->mSettings->journalTitle().isEmpty()) {
140 *ts <<
"<h1>" << d->mSettings->journalTitle() <<
"</h1>" << endl;
142 createJournalView(ts);
146 if (d->mSettings->freeBusyView()) {
147 if (!d->mSettings->freeBusyTitle().isEmpty()) {
148 *ts <<
"<h1>" << d->mSettings->freeBusyTitle() <<
"</h1>" << endl;
150 createFreeBusyView(ts);
156 *ts <<
"</body></html>" << endl;
163 QDate start = fromDate();
168 int startmonth = start.
month();
169 int startyear = start.
year();
171 while (start < toDate()) {
177 << i18nc(
"@title month and year",
"%1 %2", hMon, hYear)
179 if (KGlobal::locale()->weekStartDay() == 1) {
186 *ts <<
"<table border=\"1\">" << endl;
190 for (
int i=0; i < 7; ++i) {
191 *ts <<
"<th>" << KGlobal::locale()->calendar()->weekDayName(start.
addDays(i)) <<
"</th>";
193 *ts <<
"</tr>" << endl;
196 while (start <= end) {
197 *ts <<
" <tr>" << endl;
198 for (
int i=0; i < 7; ++i) {
199 *ts <<
" <td valign=\"top\"><table border=\"0\">";
202 if (d->mHolidayMap.contains(start) || start.
dayOfWeek() == 7) {
203 *ts <<
"class=\"dateholiday\"";
205 *ts <<
"class=\"date\"";
209 if (d->mHolidayMap.contains(start)) {
210 *ts <<
" <em>" << d->mHolidayMap[start] <<
"</em>";
213 *ts <<
"</td></tr><tr><td valign=\"top\">";
216 if (start >= fromDate() && start <= toDate()) {
217 Event::List events = d->mCalendar->events(start, d->mCalendar->timeSpec(),
220 if (events.
count()) {
222 Event::List::ConstIterator it;
224 if (checkSecrecy(*it)) {
225 createEvent(ts, *it, start,
false);
234 *ts <<
"</td></tr></table></td>" << endl;
237 *ts <<
" </tr>" << endl;
239 *ts <<
"</table>" << endl;
241 if (startmonth > 12) {
245 start.
setYMD(startyear, startmonth, 1);
253 *ts <<
"<table border=\"0\" cellpadding=\"3\" cellspacing=\"3\">" << endl;
254 *ts <<
" <tr>" << endl;
255 *ts <<
" <th class=\"sum\">" << i18nc(
"@title:column event start time",
256 "Start Time") <<
"</th>" << endl;
257 *ts <<
" <th>" << i18nc(
"@title:column event end time",
258 "End Time") <<
"</th>" << endl;
259 *ts <<
" <th>" << i18nc(
"@title:column event description",
260 "Event") <<
"</th>" << endl;
261 if (d->mSettings->eventLocation()) {
262 *ts <<
" <th>" << i18nc(
"@title:column event locatin",
263 "Location") <<
"</th>" << endl;
266 if (d->mSettings->eventCategories()) {
267 *ts <<
" <th>" << i18nc(
"@title:column event categories",
268 "Categories") <<
"</th>" << endl;
271 if (d->mSettings->eventAttendees()) {
272 *ts <<
" <th>" << i18nc(
"@title:column event attendees",
273 "Attendees") <<
"</th>" << endl;
277 *ts <<
" </tr>" << endl;
279 for (
QDate dt = fromDate(); dt <= toDate(); dt = dt.addDays(1)) {
280 kDebug() <<
"Getting events for" << dt.toString();
281 Event::List events = d->mCalendar->events(dt, d->mCalendar->timeSpec(),
284 if (events.
count()) {
286 <<
"\" class=\"datehead\"><i>"
287 << KGlobal::locale()->formatDate(dt)
288 <<
"</i></td></tr>" << endl;
290 Event::List::ConstIterator it;
292 if (checkSecrecy(*it)) {
293 createEvent(ts, *it, dt);
299 *ts <<
"</table>" << endl;
305 bool withDescription)
307 kDebug() <<
event->summary();
308 *ts <<
" <tr>" << endl;
310 if (!event->allDay()) {
311 if (event->isMultiDay(d->mCalendar->timeSpec()) && (event->dtStart().date() != date)) {
312 *ts <<
" <td> </td>" << endl;
314 *ts <<
" <td valign=\"top\">"
318 if (event->isMultiDay(d->mCalendar->timeSpec()) && (event->dtEnd().date() != date)) {
319 *ts <<
" <td> </td>" << endl;
321 *ts <<
" <td valign=\"top\">"
326 *ts <<
" <td> </td><td> </td>" << endl;
329 *ts <<
" <td class=\"sum\">" << endl;
330 *ts <<
" <b>" << cleanChars(event->summary()) <<
"</b>" << endl;
331 if (withDescription && !event->description().isEmpty()) {
332 *ts <<
" <p>" << breakString(cleanChars(event->description())) <<
"</p>" << endl;
334 *ts <<
" </td>" << endl;
336 if (d->mSettings->eventLocation()) {
337 *ts <<
" <td>" << endl;
338 formatLocation(ts, event);
339 *ts <<
" </td>" << endl;
342 if (d->mSettings->eventCategories()) {
343 *ts <<
" <td>" << endl;
344 formatCategories(ts, event);
345 *ts <<
" </td>" << endl;
348 if (d->mSettings->eventAttendees()) {
349 *ts <<
" <td>" << endl;
350 formatAttendees(ts, event);
351 *ts <<
" </td>" << endl;
354 *ts <<
" </tr>" << endl;
359 Todo::List rawTodoList = d->mCalendar->todos();
362 while (index < rawTodoList.
count()) {
365 const QString uid = ev->relatedTo();
367 Incidence::Ptr inc = d->mCalendar->incidence(uid);
368 if (inc && inc->type() == Incidence::TypeTodo) {
375 index = rawTodoList.
indexOf(subev);
382 Todo::List::ConstIterator it;
383 for (
int i = 1; i <= 9; ++i) {
385 if ((*it)->priority() == i && checkSecrecy(*it)) {
391 if ((*it)->priority() == 0 && checkSecrecy(*it)) {
397 *ts <<
"<table border=\"0\" cellpadding=\"3\" cellspacing=\"3\">" << endl;
398 *ts <<
" <tr>" << endl;
399 *ts <<
" <th class=\"sum\">" << i18nc(
"@title:column",
"To-do") <<
"</th>" << endl;
400 *ts <<
" <th>" << i18nc(
"@title:column to-do priority",
"Priority") <<
"</th>" << endl;
401 *ts <<
" <th>" << i18nc(
"@title:column to-do percent completed",
402 "Completed") <<
"</th>" << endl;
403 if (d->mSettings->taskDueDate()) {
404 *ts <<
" <th>" << i18nc(
"@title:column to-do due date",
"Due Date") <<
"</th>" << endl;
407 if (d->mSettings->taskLocation()) {
408 *ts <<
" <th>" << i18nc(
"@title:column to-do location",
"Location") <<
"</th>" << endl;
411 if (d->mSettings->taskCategories()) {
412 *ts <<
" <th>" << i18nc(
"@title:column to-do categories",
"Categories") <<
"</th>" << endl;
415 if (d->mSettings->taskAttendees()) {
416 *ts <<
" <th>" << i18nc(
"@title:column to-do attendees",
"Attendees") <<
"</th>" << endl;
419 *ts <<
" </tr>" << endl;
423 if ((*it)->relatedTo().isEmpty()) {
430 Incidence::List relations = d->mCalendar->relations((*it)->uid());
432 if (relations.count()) {
434 *ts <<
" <tr>" << endl;
435 *ts <<
" <td class=\"subhead\" colspan=";
437 *ts <<
"><a name=\"sub" << (*it)->uid() <<
"\"></a>"
438 << i18nc(
"@title:column sub-to-dos of the parent to-do",
439 "Sub-To-dos of: ") <<
"<a href=\"#"
440 << (*it)->uid() <<
"\"><b>" << cleanChars((*it)->summary())
441 <<
"</b></a></td>" << endl;
442 *ts <<
" </tr>" << endl;
447 for (
int i = 1; i <= 9; ++i) {
448 Incidence::List::ConstIterator it2;
449 for (it2 = relations.constBegin(); it2 != relations.constEnd(); ++it2) {
451 if (ev3 && ev3->priority() == i) {
456 Incidence::List::ConstIterator it2;
457 for (it2 = relations.constBegin(); it2 != relations.constEnd(); ++it2) {
459 if (ev3 && ev3->priority() == 0) {
464 Todo::List::ConstIterator it3;
466 createTodo(ts, *it3);
471 *ts <<
"</table>" << endl;
478 const bool completed = todo->isCompleted();
480 Incidence::List relations = d->mCalendar->relations(todo->uid());
482 *ts <<
"<tr>" << endl;
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;
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;
497 *ts <<
" </td>" << endl;
501 *ts <<
" class=\"done\"";
504 *ts <<
" " << todo->priority() << endl;
505 *ts <<
" </td>" << endl;
509 *ts <<
" class=\"done\"";
512 *ts <<
" " << i18nc(
"@info/plain to-do percent complete",
513 "%1 %", todo->percentComplete()) << endl;
514 *ts <<
" </td>" << endl;
516 if (d->mSettings->taskDueDate()) {
519 *ts <<
" class=\"done\"";
522 if (todo->hasDueDate()) {
525 *ts <<
" " << endl;
527 *ts <<
" </td>" << endl;
530 if (d->mSettings->taskLocation()) {
533 *ts <<
" class=\"done\"";
536 formatLocation(ts, todo);
537 *ts <<
" </td>" << endl;
540 if (d->mSettings->taskCategories()) {
543 *ts <<
" class=\"done\"";
546 formatCategories(ts, todo);
547 *ts <<
" </td>" << endl;
550 if (d->mSettings->taskAttendees()) {
553 *ts <<
" class=\"done\"";
556 formatAttendees(ts, todo);
557 *ts <<
" </td>" << endl;
560 *ts <<
"</tr>" << endl;
569 void HtmlExport::createJournalView(
QTextStream *ts)
576 void HtmlExport::createFreeBusyView(
QTextStream *ts)
582 bool HtmlExport::checkSecrecy(
const Incidence::Ptr &incidence)
584 int secrecy = incidence->secrecy();
585 if (secrecy == Incidence::SecrecyPublic) {
588 if (secrecy == Incidence::SecrecyPrivate && !d->mSettings->excludePrivate()) {
591 if (secrecy == Incidence::SecrecyConfidential &&
592 !d->mSettings->excludeConfidential()) {
599 const Incidence::Ptr &incidence)
601 if (!incidence->location().isEmpty()) {
602 *ts <<
" " << cleanChars(incidence->location()) << endl;
604 *ts <<
" " << endl;
609 const Incidence::Ptr &incidence)
611 if (!incidence->categoriesStr().isEmpty()) {
612 *ts <<
" " << cleanChars(incidence->categoriesStr()) << endl;
614 *ts <<
" " << endl;
619 const Incidence::Ptr &incidence)
622 if (attendees.
count()) {
624 *ts << incidence->organizer()->fullName();
625 *ts <<
"</em><br />";
626 Attendee::List::ConstIterator it;
629 if (!a->email().isEmpty()) {
630 *ts <<
"<a href=\"mailto:" << a->email();
631 *ts <<
"\">" << cleanChars(a->name()) <<
"</a>";
633 *ts <<
" " << cleanChars(a->name());
635 *ts <<
"<br />" << endl;
638 *ts <<
" " << endl;
652 for (
int i = 0; i <= number; ++i) {
654 tmp = tmpText.
left(pos);
655 tmpText = tmpText.
right(tmpText.
length() - pos - 1);
665 QString trailer = i18nc(
"@info/plain",
"This page was created ");
671 if (!d->mSettings->eMail().isEmpty()) {
672 if (!d->mSettings->name().isEmpty()) {
673 trailer += i18nc(
"@info/plain page creator email link with name",
674 "by <link url='mailto:%1'>%2</link> ",
675 d->mSettings->eMail(), d->mSettings->name());
677 trailer += i18nc(
"@info/plain page creator email link",
678 "by <link url='mailto:%1'>%2</link> ",
679 d->mSettings->eMail(), d->mSettings->eMail());
682 if (!d->mSettings->name().isEmpty()) {
683 trailer += i18nc(
"@info/plain page creator name only",
684 "by %1 ", d->mSettings->name());
687 if (!d->mSettings->creditName().isEmpty()) {
688 if (!d->mSettings->creditURL().isEmpty()) {
689 trailer += i18nc(
"@info/plain page credit with name and link",
690 "with <link url='%1'>%2</link>",
691 d->mSettings->creditURL(), d->mSettings->creditName());
693 trailer += i18nc(
"@info/plain page credit name only",
694 "with %1", d->mSettings->creditName());
697 *ts <<
"<p>" << trailer <<
"</p>" << endl;
720 QString HtmlExport::styleSheet()
const
722 if (!d->mSettings->styleSheet().isEmpty()) {
723 return d->mSettings->styleSheet();
729 css +=
QLatin1String(
" body { background-color:white; color:black; direction: rtl }\n");
730 css +=
QLatin1String(
" td { text-align:center; background-color:#eee }\n");
731 css +=
QLatin1String(
" th { text-align:center; background-color:#228; color:white }\n");
732 css +=
QLatin1String(
" td.sumdone { background-color:#ccc }\n");
733 css +=
QLatin1String(
" td.done { background-color:#ccc }\n");
734 css +=
QLatin1String(
" td.subhead { text-align:center; background-color:#ccf }\n");
735 css +=
QLatin1String(
" td.datehead { text-align:center; background-color:#ccf }\n");
736 css +=
QLatin1String(
" td.space { background-color:white }\n");
739 css +=
QLatin1String(
" body { background-color:white; color:black }\n");
740 css +=
QLatin1String(
" td { text-align:center; background-color:#eee }\n");
741 css +=
QLatin1String(
" th { text-align:center; background-color:#228; color:white }\n");
743 css +=
QLatin1String(
" td.sumdone { text-align:left; background-color:#ccc }\n");
744 css +=
QLatin1String(
" td.done { background-color:#ccc }\n");
745 css +=
QLatin1String(
" td.subhead { text-align:center; background-color:#ccf }\n");
746 css +=
QLatin1String(
" td.datehead { text-align:center; background-color:#ccf }\n");
747 css +=
QLatin1String(
" td.space { background-color:white }\n");
749 css +=
QLatin1String(
" td.dateholiday { text-align:left; color:red }\n");
755 void HtmlExport::addHoliday(
const QDate &date,
const QString &name)
757 if (d->mHolidayMap[date].isEmpty()) {
758 d->mHolidayMap[date] = name;
760 d->mHolidayMap[date] = i18nc(
"@info/plain holiday by date and name",
761 "%1, %2", d->mHolidayMap[date], name);
765 QDate HtmlExport::fromDate()
const
767 return d->mSettings->dateStart().date();
770 QDate HtmlExport::toDate()
const
772 return d->mSettings->dateEnd().date();
void setCodec(QTextCodec *codec)
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const
bool save(const QString &fileName=QString())
Writes out the calendar in HTML format.
void append(const T &value)
int indexOf(const T &value, int from) const
const_iterator constEnd() const
KCALUTILS_EXPORT QString formatDate(const KDateTime &dt, bool shortfmt=true, const KDateTime::Spec &spec=KDateTime::Spec())
Build a QString date representation of a KDateTime object.
QString number(int n, int base)
bool contains(const T &value) const
QString fromUtf8(const char *str, int size)
bool setYMD(int y, int m, int d)
This file is part of the API for handling calendar data and provides static functions for formatting ...
virtual bool open(QFlags< QIODevice::OpenModeFlag > mode)
QString right(int n) const
QString & replace(int position, int n, QChar after)
const_iterator constBegin() const
KCALUTILS_EXPORT QString formatTime(const KDateTime &dt, bool shortfmt=true, const KDateTime::Spec &spec=KDateTime::Spec())
Build a QString time representation of a KDateTime object.
int count(const T &value) const
QString left(int n) const
QDate addDays(int ndays) const
QSharedPointer< X > staticCast() const