KCalUtils

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

KDE's Doxygen guidelines are available online.