diff --git a/kcal/htmlexport.cpp b/kcal/htmlexport.cpp index 50db02550..4aab03fbf 100644 --- a/kcal/htmlexport.cpp +++ b/kcal/htmlexport.cpp @@ -1,777 +1,779 @@ /* This file is part of the kcal library. Copyright (c) 2000,2001 Cornelius Schumacher Copyright (C) 2004 Reinhold Kainhofer This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "htmlexport.h" #include "htmlexportsettings.h" #include "calendar.h" #include "event.h" #include "todo.h" #ifndef KORG_NOKABC #include "kabc/stdaddressbook.h" #endif #include #include #include #include #include #include #include #include #include #include using namespace KCal; static QString cleanChars( const QString &txt ); //@cond PRIVATE class KCal::HtmlExport::Private { public: Private( Calendar *calendar, HTMLExportSettings *settings ) : mCalendar( calendar ), mSettings( settings ) {} Calendar *mCalendar; HTMLExportSettings *mSettings; QMap mHolidayMap; }; //@endcond HtmlExport::HtmlExport( Calendar *calendar, HTMLExportSettings *settings ) : d( new Private( calendar, settings ) ) { } HtmlExport::~HtmlExport() { delete d; } bool HtmlExport::save( const QString &fileName ) { QString fn( fileName ); if ( fn.isEmpty() && d->mSettings ) { fn = d->mSettings->outputFile(); } if ( !d->mSettings || fn.isEmpty() ) { return false; } QFile f( fileName ); if ( !f.open( QIODevice::WriteOnly ) ) { return false; } QTextStream ts( &f ); bool success = save( &ts ); f.close(); return success; } bool HtmlExport::save( QTextStream *ts ) { if ( !d->mSettings ) { return false; } ts->setCodec( "UTF-8" ); // Write HTML header *ts << "\n"; *ts << "" << endl; *ts << " \n"; if ( !d->mSettings->pageTitle().isEmpty() ) { *ts << " " << d->mSettings->pageTitle() << "\n"; } *ts << " \n"; *ts << "\n"; // FIXME: Write header // (Heading, Calendar-Owner, Calendar-Date, ...) if ( d->mSettings->eventView() || d->mSettings->monthView() || d->mSettings->weekView() ) { if ( !d->mSettings->eventTitle().isEmpty() ) { *ts << "

" << d->mSettings->eventTitle() << "

\n"; } // Write Week View if ( d->mSettings->weekView() ) { createWeekView( ts ); } // Write Month View if ( d->mSettings->monthView() ) { createMonthView( ts ); } // Write Event List if ( d->mSettings->eventView() ) { createEventList( ts ); } } // Write Todo List if ( d->mSettings->todoView() ) { if ( !d->mSettings->todoListTitle().isEmpty() ) { *ts << "

" << d->mSettings->todoListTitle() << "

\n"; } createTodoList( ts ); } // Write Journals if ( d->mSettings->journalView() ) { if ( !d->mSettings->journalTitle().isEmpty() ) { *ts << "

" << d->mSettings->journalTitle() << "

\n"; } createJournalView( ts ); } // Write Free/Busy if ( d->mSettings->freeBusyView() ) { if ( !d->mSettings->freeBusyTitle().isEmpty() ) { *ts << "

" << d->mSettings->freeBusyTitle() << "

\n"; } createFreeBusyView( ts ); } createFooter( ts ); // Write HTML trailer *ts << "\n"; return true; } void HtmlExport::createMonthView( QTextStream *ts ) { QDate start = fromDate(); start.setYMD( start.year(), start.month(), 1 ); // go back to first day in month QDate end( start.year(), start.month(), start.daysInMonth() ); int startmonth = start.month(); int startyear = start.year(); while ( start < toDate() ) { // Write header + QDate hDate( start.year(), start.month(), 1 ); + QString hMon = hDate.toString( "MMMM" ); + QString hYear = hDate.toString( "yyyy" ); *ts << "

" - << i18nc( "@title month and year", "%1 %2", - KGlobal::locale()->calendar()->monthName( start ), start.year() ) + << i18nc( "@title month and year", "%1 %2", hMon, hYear ) << "

\n"; if ( KGlobal::locale()->weekStartDay() == 1 ) { start = start.addDays( 1 - start.dayOfWeek() ); } else { if ( start.dayOfWeek() != 7 ) { start = start.addDays( -start.dayOfWeek() ); } } *ts << "\n"; // Write table header *ts << " "; for ( int i=0; i < 7; ++i ) { *ts << ""; } *ts << "\n"; // Write days while ( start <= end ) { *ts << " \n"; for ( int i=0; i < 7; ++i ) { *ts << " \n"; start = start.addDays( 1 ); } *ts << " \n"; } *ts << "
" << KGlobal::locale()->calendar()->weekDayName( start.addDays(i) ) << "
"; *ts << "
mHolidayMap.contains( start ) || start.dayOfWeek() == 7 ) { *ts << "class=\"dateholiday\""; } else { *ts << "class=\"date\""; } *ts << ">" << QString::number( start.day() ); if ( d->mHolidayMap.contains( start ) ) { *ts << " " << d->mHolidayMap[start] << ""; } *ts << "
"; Event::List events = d->mCalendar->events( start, d->mCalendar->timeSpec(), EventSortStartDate, SortDirectionAscending ); if ( events.count() ) { *ts << ""; Event::List::ConstIterator it; for ( it = events.begin(); it != events.end(); ++it ) { if ( checkSecrecy( *it ) ) { createEvent( ts, *it, start, false ); } } *ts << "
"; } else { *ts << " "; } *ts << "
\n"; startmonth += 1; if ( startmonth > 12 ) { startyear += 1; startmonth = 1; } start.setYMD( startyear, startmonth, 1 ); end.setYMD( start.year(), start.month(), start.daysInMonth() ); } } void HtmlExport::createEventList( QTextStream *ts ) { int columns = 3; *ts << "\n"; *ts << " \n"; *ts << " \n"; *ts << " \n"; *ts << " \n"; if ( d->mSettings->eventLocation() ) { *ts << " \n"; ++columns; } if ( d->mSettings->eventCategories() ) { *ts << " \n"; ++columns; } if ( d->mSettings->eventAttendees() ) { *ts << " \n"; ++columns; } *ts << " \n"; for ( QDate dt = fromDate(); dt <= toDate(); dt = dt.addDays(1) ) { kDebug() << "Getting events for" << dt.toString(); Event::List events = d->mCalendar->events( dt, d->mCalendar->timeSpec(), EventSortStartDate, SortDirectionAscending ); if ( events.count() ) { *ts << " \n"; Event::List::ConstIterator it; for ( it = events.begin(); it != events.end(); ++it ) { if ( checkSecrecy( *it ) ) { createEvent( ts, *it, dt ); } } } } *ts << "
" << i18nc( "@title:column event start time", "Start Time" ) << "" << i18nc( "@title:column event end time", "End Time" ) << "" << i18nc( "@title:column event description", "Event" ) << "" << i18nc( "@title:column event locatin", "Location" ) << "" << i18nc( "@title:column event categories", "Categories" ) << "" << i18nc( "@title:column event attendees", "Attendees" ) << "
" << KGlobal::locale()->formatDate( dt ) << "
\n"; } void HtmlExport::createEvent ( QTextStream *ts, Event *event, QDate date, bool withDescription ) { kDebug() << event->summary(); *ts << " \n"; if ( !event->allDay() ) { if ( event->isMultiDay( d->mCalendar->timeSpec() ) && ( event->dtStart().date() != date ) ) { *ts << "  \n"; } else { *ts << " " << event->dtStartTimeStr( true, d->mCalendar->timeSpec() ) << "\n"; } if ( event->isMultiDay( d->mCalendar->timeSpec() ) && ( event->dtEnd().date() != date ) ) { *ts << "  \n"; } else { *ts << " " << event->dtEndTimeStr( true, d->mCalendar->timeSpec() ) << "\n"; } } else { *ts << "   \n"; } *ts << " \n"; *ts << " " << cleanChars( event->summary() ) << "\n"; if ( withDescription && !event->description().isEmpty() ) { *ts << "

" << breakString( cleanChars( event->description() ) ) << "

\n"; } *ts << " \n"; if ( d->mSettings->eventLocation() ) { *ts << " \n"; formatLocation( ts, event ); *ts << " \n"; } if ( d->mSettings->eventCategories() ) { *ts << " \n"; formatCategories( ts, event ); *ts << " \n"; } if ( d->mSettings->eventAttendees() ) { *ts << " \n"; formatAttendees( ts, event ); *ts << " \n"; } *ts << " \n"; } void HtmlExport::createTodoList ( QTextStream *ts ) { Todo::List rawTodoList = d->mCalendar->todos(); int index = 0; while ( index < rawTodoList.count() ) { Todo *ev = rawTodoList[ index ]; Todo *subev = ev; if ( ev->relatedTo() ) { if ( ev->relatedTo()->type() == "Todo" ) { if ( !rawTodoList.contains( static_cast( ev->relatedTo() ) ) ) { rawTodoList.append( static_cast( ev->relatedTo() ) ); } } } index = rawTodoList.indexOf( subev ); ++index; } // FIXME: Sort list by priorities. This is brute force and should be // replaced by a real sorting algorithm. Todo::List todoList; Todo::List::ConstIterator it; for ( int i = 1; i <= 9; ++i ) { for ( it = rawTodoList.begin(); it != rawTodoList.end(); ++it ) { if ( (*it)->priority() == i && checkSecrecy( *it ) ) { todoList.append( *it ); } } } for ( it = rawTodoList.begin(); it != rawTodoList.end(); ++it ) { if ( (*it)->priority() == 0 && checkSecrecy( *it ) ) { todoList.append( *it ); } } int columns = 3; *ts << "\n"; *ts << " \n"; *ts << " \n"; *ts << " \n"; *ts << " \n"; if ( d->mSettings->taskDueDate() ) { *ts << " \n"; ++columns; } if ( d->mSettings->taskLocation() ) { *ts << " \n"; ++columns; } if ( d->mSettings->taskCategories() ) { *ts << " \n"; ++columns; } if ( d->mSettings->taskAttendees() ) { *ts << " \n"; ++columns; } *ts << " \n"; // Create top-level list. for ( it = todoList.begin(); it != todoList.end(); ++it ) { if ( !(*it)->relatedTo() ) { createTodo( ts, *it ); } } // Create sub-level lists for ( it = todoList.begin(); it != todoList.end(); ++it ) { Incidence::List relations = (*it)->relations(); if ( relations.count() ) { // Generate sub-to-do list *ts << " \n"; *ts << " \n"; *ts << " \n"; Todo::List sortedList; // FIXME: Sort list by priorities. This is brute force and should be // replaced by a real sorting algorithm. for ( int i = 1; i <= 9; ++i ) { Incidence::List::ConstIterator it2; for ( it2 = relations.begin(); it2 != relations.end(); ++it2 ) { Todo *ev3 = dynamic_cast( *it2 ); if ( ev3 && ev3->priority() == i ) { sortedList.append( ev3 ); } } } Incidence::List::ConstIterator it2; for ( it2 = relations.begin(); it2 != relations.end(); ++it2 ) { Todo *ev3 = dynamic_cast( *it2 ); if ( ev3 && ev3->priority() == 0 ) { sortedList.append( ev3 ); } } Todo::List::ConstIterator it3; for ( it3 = sortedList.begin(); it3 != sortedList.end(); ++it3 ) { createTodo( ts, *it3 ); } } } *ts << "
" << i18nc( "@title:column", "To-do" ) << "" << i18nc( "@title:column to-do priority", "Priority" ) << "" << i18nc( "@title:column to-do percent completed", "Completed" ) << "" << i18nc( "@title:column to-do due date", "Due Date" ) << "" << i18nc( "@title:column to-do location", "Location" ) << "" << i18nc( "@title:column to-do categories", "Categories" ) << "" << i18nc( "@title:column to-do attendees", "Attendees" ) << "
uid() << "\">" << i18nc( "@title:column sub-to-dos of the parent to-do", "Sub-To-dos of: " ) << "uid() << "\">" << cleanChars( (*it)->summary() ) << "
\n"; } void HtmlExport::createTodo( QTextStream *ts, Todo *todo ) { kDebug(); bool completed = todo->isCompleted(); Incidence::List relations = todo->relations(); *ts << "\n"; *ts << " \n"; *ts << " uid() << "\">\n"; *ts << " " << cleanChars( todo->summary() ) << "\n"; if ( !todo->description().isEmpty() ) { *ts << "

" << breakString( cleanChars( todo->description() ) ) << "

\n"; } if ( relations.count() ) { *ts << " \n"; } *ts << " \n"; *ts << " \n"; *ts << " " << todo->priority() << "\n"; *ts << " \n"; *ts << " \n"; *ts << " " << i18nc( "@info/plain to-do percent complete", "%1 %", todo->percentComplete() ) << "\n"; *ts << " \n"; if ( d->mSettings->taskDueDate() ) { *ts << " \n"; if ( todo->hasDueDate() ) { *ts << " " << todo->dtDueDateStr() << "\n"; } else { *ts << "  \n"; } *ts << " \n"; } if ( d->mSettings->taskLocation() ) { *ts << " \n"; formatLocation( ts, todo ); *ts << " \n"; } if ( d->mSettings->taskCategories() ) { *ts << " \n"; formatCategories( ts, todo ); *ts << " \n"; } if ( d->mSettings->taskAttendees() ) { *ts << " \n"; formatAttendees( ts, todo ); *ts << " \n"; } *ts << "\n"; } void HtmlExport::createWeekView( QTextStream *ts ) { Q_UNUSED( ts ); // FIXME: Implement this! } void HtmlExport::createJournalView( QTextStream *ts ) { Q_UNUSED( ts ); // Journal::List rawJournalList = d->mCalendar->journals(); // FIXME: Implement this! } void HtmlExport::createFreeBusyView( QTextStream *ts ) { Q_UNUSED( ts ); // FIXME: Implement this! } bool HtmlExport::checkSecrecy( Incidence *incidence ) { int secrecy = incidence->secrecy(); if ( secrecy == Incidence::SecrecyPublic ) { return true; } if ( secrecy == Incidence::SecrecyPrivate && !d->mSettings->excludePrivate() ) { return true; } if ( secrecy == Incidence::SecrecyConfidential && !d->mSettings->excludeConfidential() ) { return true; } return false; } void HtmlExport::formatLocation( QTextStream *ts, Incidence *incidence ) { if ( !incidence->location().isEmpty() ) { *ts << " " << cleanChars( incidence->location() ) << "\n"; } else { *ts << "  \n"; } } void HtmlExport::formatCategories( QTextStream *ts, Incidence *incidence ) { if ( !incidence->categoriesStr().isEmpty() ) { *ts << " " << cleanChars( incidence->categoriesStr() ) << "\n"; } else { *ts << "  \n"; } } void HtmlExport::formatAttendees( QTextStream *ts, Incidence *incidence ) { Attendee::List attendees = incidence->attendees(); if ( attendees.count() ) { *ts << ""; #ifndef KORG_NOKABC KABC::AddressBook *add_book = KABC::StdAddressBook::self( true ); KABC::Addressee::List addressList; addressList = add_book->findByEmail( incidence->organizer().email() ); if ( !addressList.isEmpty() ) { KABC::Addressee o = addressList.first(); if ( !o.isEmpty() && addressList.size() < 2 ) { *ts << "organizer().email() << "\">"; *ts << cleanChars( o.formattedName() ) << "\n"; } else { *ts << incidence->organizer().fullName(); } } #else *ts << incidence->organizer().fullName(); #endif *ts << "
"; Attendee::List::ConstIterator it; for ( it = attendees.begin(); it != attendees.end(); ++it ) { Attendee *a = *it; if ( !a->email().isEmpty() ) { *ts << "email(); *ts << "\">" << cleanChars( a->name() ) << ""; } else { *ts << " " << cleanChars( a->name() ); } *ts << "
" << "\n"; } } else { *ts << "  \n"; } } QString HtmlExport::breakString( const QString &text ) { int number = text.count( "\n" ); if ( number <= 0 ) { return text; } else { QString out; QString tmpText = text; int pos = 0; QString tmp; for ( int i=0; i<=number; i++ ) { pos = tmpText.indexOf( "\n" ); tmp = tmpText.left( pos ); tmpText = tmpText.right( tmpText.length() - pos - 1 ); out += tmp + "
"; } return out; } } void HtmlExport::createFooter( QTextStream *ts ) { // FIXME: Implement this in a translatable way! QString trailer = i18nc( "@info/plain", "This page was created " ); /* bool hasPerson = false; bool hasCredit = false; bool hasCreditURL = false; QString mail, name, credit, creditURL;*/ if ( !d->mSettings->eMail().isEmpty() ) { if ( !d->mSettings->name().isEmpty() ) { trailer += i18nc( "@info/plain page creator email link with name", "by %2", d->mSettings->eMail(), d->mSettings->name() ); } else { trailer += i18nc( "@info/plain page creator email link", "by %2", d->mSettings->eMail(), d->mSettings->eMail() ); } } else { if ( !d->mSettings->name().isEmpty() ) { trailer += i18nc( "@info/plain page creator name only", "by %1 ", d->mSettings->name() ); } } if ( !d->mSettings->creditName().isEmpty() ) { if ( !d->mSettings->creditURL().isEmpty() ) { trailer += i18nc( "@info/plain page credit with name and link", "with %2", d->mSettings->creditURL(), d->mSettings->creditName() ); } else { trailer += i18nc( "@info/plain page credit name only", "with %1", d->mSettings->creditName() ); } } *ts << "

" << trailer << "

\n"; } QString cleanChars( const QString &text ) { QString txt = text; txt = txt.replace( "&", "&" ); txt = txt.replace( "<", "<" ); txt = txt.replace( ">", ">" ); txt = txt.replace( "\"", """ ); txt = txt.replace( QString::fromUtf8( "ä" ), "ä" ); txt = txt.replace( QString::fromUtf8( "Ä" ), "Ä" ); txt = txt.replace( QString::fromUtf8( "ö" ), "ö" ); txt = txt.replace( QString::fromUtf8( "Ö" ), "Ö" ); txt = txt.replace( QString::fromUtf8( "ü" ), "ü" ); txt = txt.replace( QString::fromUtf8( "Ü" ), "Ü" ); txt = txt.replace( QString::fromUtf8( "ß" ), "ß" ); txt = txt.replace( QString::fromUtf8( "€" ), "€" ); txt = txt.replace( QString::fromUtf8( "é" ), "é" ); return txt; } QString HtmlExport::styleSheet() const { if ( !d->mSettings->styleSheet().isEmpty() ) { return d->mSettings->styleSheet(); } QString css; if ( QApplication::isRightToLeft() ) { css += " body { background-color:white; color:black; direction: rtl }\n"; css += " td { text-align:center; background-color:#eee }\n"; css += " th { text-align:center; background-color:#228; color:white }\n"; css += " td.sumdone { background-color:#ccc }\n"; css += " td.done { background-color:#ccc }\n"; css += " td.subhead { text-align:center; background-color:#ccf }\n"; css += " td.datehead { text-align:center; background-color:#ccf }\n"; css += " td.space { background-color:white }\n"; css += " td.dateholiday { color:red }\n"; } else { css += " body { background-color:white; color:black }\n"; css += " td { text-align:center; background-color:#eee }\n"; css += " th { text-align:center; background-color:#228; color:white }\n"; css += " td.sum { text-align:left }\n"; css += " td.sumdone { text-align:left; background-color:#ccc }\n"; css += " td.done { background-color:#ccc }\n"; css += " td.subhead { text-align:center; background-color:#ccf }\n"; css += " td.datehead { text-align:center; background-color:#ccf }\n"; css += " td.space { background-color:white }\n"; css += " td.date { text-align:left }\n"; css += " td.dateholiday { text-align:left; color:red }\n"; } return css; } void HtmlExport::addHoliday( const QDate &date, const QString &name ) { if ( d->mHolidayMap[date].isEmpty() ) { d->mHolidayMap[date] = name; } else { d->mHolidayMap[date] = i18nc( "@info/plain holiday by date and name", "%1, %2", d->mHolidayMap[date], name ); } } QDate HtmlExport::fromDate() const { return d->mSettings->dateStart().date(); } QDate HtmlExport::toDate() const { return d->mSettings->dateEnd().date(); }