/* This file is part of libkcal. 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef KORG_NOKABC #include #endif #include "htmlexport.h" #include "htmlexportsettings.h" using namespace KCal; HtmlExport::HtmlExport( Calendar *calendar, HTMLExportSettings *settings ) : mCalendar( calendar ), mSettings( settings ) { } bool HtmlExport::save( const TQString &fileName ) { TQString fn( fileName ); if ( fn.isEmpty() && mSettings ) { fn = mSettings->outputFile(); } if ( !mSettings || fn.isEmpty() ) { return false; } TQFile f( fileName ); if ( !f.open(IO_WriteOnly)) { return false; } TQTextStream ts(&f); bool success = save(&ts); f.close(); return success; } bool HtmlExport::save(TQTextStream *ts) { if ( !mSettings ) return false; ts->setEncoding( TQTextStream::UnicodeUTF8 ); // Write HTML header *ts << "\n"; *ts << "" << endl; *ts << " \n"; if ( !mSettings->pageTitle().isEmpty()) *ts << " " << mSettings->pageTitle() << "\n"; *ts << " \n"; *ts << "\n"; // FIXME: Write header // (Heading, Calendar-Owner, Calendar-Date, ...) if ( mSettings->eventView() || mSettings->monthView() || mSettings->weekView() ) { if (!mSettings->eventTitle().isEmpty()) *ts << "

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

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

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

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

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

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

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

\n"; createFreeBusyView(ts); } createFooter( ts ); // Write HTML trailer *ts << "\n"; return true; } void HtmlExport::createMonthView(TQTextStream *ts) { TQDate start = fromDate(); start.setYMD( start.year(), start.month(), 1 ); // go back to first day in month TQDate end( start.year(), start.month(), start.daysInMonth() ); int startmonth = start.month(); int startyear = start.year(); while ( start < toDate() ) { // Write header *ts << "

" << (i18n("month_year","%1 %2").arg(TDEGlobal::locale()->calendar()->monthName(start)) .arg(start.year())) << "

\n"; if ( TDEGlobal::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 << "
" << TDEGlobal::locale()->calendar()->weekDayName( start.addDays(i) ) << "
"; *ts << "
" << TQString::number(start.day()); if (mHolidayMap.contains(start)) { *ts << " " << mHolidayMap[start] << ""; } *ts << "
"; // Only print events within the from-to range if ( start >= fromDate() && start <= toDate() ) { Event::List events = mCalendar->events( start, 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 (TQTextStream *ts) { int columns = 3; *ts << "\n"; *ts << " \n"; *ts << " \n"; *ts << " \n"; *ts << " \n"; if ( mSettings->eventLocation() ) { *ts << " \n"; ++columns; } if ( mSettings->eventCategories() ) { *ts << " \n"; ++columns; } if ( mSettings->eventAttendees() ) { *ts << " \n"; ++columns; } *ts << " \n"; for ( TQDate dt = fromDate(); dt <= toDate(); dt = dt.addDays(1) ) { kdDebug(5850) << "Getting events for " << TQString(dt.toString()) << endl; Event::List events = mCalendar->events(dt, EventSortStartDate, SortDirectionAscending ); if (events.count()) { Event::List::ConstIterator it; bool first = true; for( it = events.begin(); it != events.end(); ++it ) { if ( checkSecrecy( *it ) ) { if ( first ) { *ts << " \n"; first = false; } createEvent( ts, *it, dt ); } } } } *ts << "
" << i18n("Start Time") << "" << i18n("End Time") << "" << i18n("Event") << "" << i18n("Location") << "" << i18n("Categories") << "" << i18n("Attendees") << "
" << TDEGlobal::locale()->formatDate(dt) << "
\n"; } void HtmlExport::createEvent (TQTextStream *ts, Event *event, TQDate date,bool withDescription) { kdDebug(5850) << "HtmlExport::createEvent(): " << event->summary() << endl; *ts << " \n"; if (!event->doesFloat()) { if (event->isMultiDay() && (event->dtStart().date() != date)) { *ts << "  \n"; } else { *ts << " " << IncidenceFormatter::timeToString( event->dtStart(), true ) << "\n"; } if (event->isMultiDay() && (event->dtEnd().date() != date)) { *ts << "  \n"; } else { *ts << " " << IncidenceFormatter::timeToString( event->dtEnd(), true ) << "\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 ( mSettings->eventLocation() ) { *ts << " \n"; formatLocation( ts, event ); *ts << " \n"; } if ( mSettings->eventCategories() ) { *ts << " \n"; formatCategories( ts, event ); *ts << " \n"; } if ( mSettings->eventAttendees() ) { *ts << " \n"; formatAttendees( ts, event ); *ts << " \n"; } *ts << " \n"; } void HtmlExport::createTodoList ( TQTextStream *ts ) { Todo::List rawTodoList = mCalendar->todos(); Todo::List::Iterator it = rawTodoList.begin(); while ( it != rawTodoList.end() ) { Todo *ev = *it; Todo *subev = ev; if ( ev->relatedTo() ) { if ( ev->relatedTo()->type()=="Todo" ) { if ( rawTodoList.find( static_cast( ev->relatedTo() ) ) == rawTodoList.end() ) { rawTodoList.append( static_cast( ev->relatedTo() ) ); } } } it = rawTodoList.find( subev ); ++it; } // FIXME: Sort list by priorities. This is brute force and should be // replaced by a real sorting algorithm. Todo::List todoList; 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 ( mSettings->taskDueDate() ) { *ts << " \n"; ++columns; } if ( mSettings->taskLocation() ) { *ts << " \n"; ++columns; } if ( mSettings->taskCategories() ) { *ts << " \n"; ++columns; } if ( 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-task list of event ev *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 << "
" << i18n("Task") << "" << i18n("Priority") << "" << i18n("Completed") << "" << i18n("Due Date") << "" << i18n("Location") << "" << i18n("Categories") << "" << i18n("Attendees") << "
uid() << "\">" << i18n("Sub-Tasks of: ") << "uid() << "\">" << cleanChars( (*it)->summary()) << "
\n"; } void HtmlExport::createTodo (TQTextStream *ts,Todo *todo) { kdDebug(5850) << "HtmlExport::createTodo()" << endl; 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 << " " << i18n("%1 %").arg(todo->percentComplete()) << "\n"; *ts << " \n"; if ( mSettings->taskDueDate() ) { *ts << " \n"; if (todo->hasDueDate()) { *ts << " " << IncidenceFormatter::dateToString( todo->dtDue( true ) ) << "\n"; } else { *ts << "  \n"; } *ts << " \n"; } if ( mSettings->taskLocation() ) { *ts << " \n"; formatLocation(ts,todo); *ts << " \n"; } if ( mSettings->taskCategories() ) { *ts << " \n"; formatCategories(ts,todo); *ts << " \n"; } if ( mSettings->taskAttendees() ) { *ts << " \n"; formatAttendees(ts,todo); *ts << " \n"; } *ts << "\n"; } void HtmlExport::createWeekView( TQTextStream */*ts*/ ) { // FIXME: Implement this! } void HtmlExport::createJournalView( TQTextStream */*ts*/ ) { // Journal::List rawJournalList = mCalendar->journals(); // FIXME: Implement this! } void HtmlExport::createFreeBusyView( TQTextStream */*ts*/ ) { // FIXME: Implement this! } bool HtmlExport::checkSecrecy( Incidence *incidence ) { int secrecy = incidence->secrecy(); if ( secrecy == Incidence::SecrecyPublic ) { return true; } if ( secrecy == Incidence::SecrecyPrivate && !mSettings->excludePrivate() ) { return true; } if ( secrecy == Incidence::SecrecyConfidential && !mSettings->excludeConfidential() ) { return true; } return false; } void HtmlExport::formatLocation (TQTextStream *ts,Incidence *event) { if (!event->location().isEmpty()) { *ts << " " << cleanChars(event->location()) << "\n"; } else { *ts << "  \n"; } } void HtmlExport::formatCategories (TQTextStream *ts,Incidence *event) { if (!event->categoriesStr().isEmpty()) { *ts << " " << cleanChars(event->categoriesStr()) << "\n"; } else { *ts << "  \n"; } } void HtmlExport::formatAttendees( TQTextStream *ts, Incidence *event ) { Attendee::List attendees = event->attendees(); if (attendees.count()) { *ts << ""; #ifndef KORG_NOKABC TDEABC::AddressBook *add_book = TDEABC::StdAddressBook::self( true ); TDEABC::Addressee::List addressList; addressList = add_book->findByEmail(event->organizer().email()); TDEABC::Addressee o = addressList.first(); if (!o.isEmpty() && addressList.size()<2) { *ts << "organizer().email() << "\">"; *ts << cleanChars(o.formattedName()) << "\n"; } else *ts << event->organizer().fullName(); #else *ts << event->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"; } } TQString HtmlExport::breakString(const TQString &text) { int number = text.contains("\n"); if(number < 0) { return text; } else { TQString out; TQString tmpText = text; int pos = 0; TQString tmp; for(int i=0;i<=number;i++) { pos = tmpText.find("\n"); tmp = tmpText.left(pos); tmpText = tmpText.right(tmpText.length() - pos - 1); out += tmp + "
"; } return out; } } void HtmlExport::createFooter( TQTextStream *ts ) { // FIXME: Implement this in a translatable way! TQString trailer = i18n("This page was created "); /* bool hasPerson = false; bool hasCredit = false; bool hasCreditURL = false; TQString mail, name, credit, creditURL;*/ if (!mSettings->eMail().isEmpty()) { if (!mSettings->name().isEmpty()) trailer += i18n("by %2 ").arg( mSettings->eMail() ).arg( mSettings->name() ); else trailer += i18n("by %2 ").arg( mSettings->eMail() ).arg( mSettings->eMail() ); } else { if (!mSettings->name().isEmpty()) trailer += i18n("by %1 ").arg( mSettings->name() ); } if (!mSettings->creditName().isEmpty()) { if (!mSettings->creditURL().isEmpty()) trailer += i18n("with %2") .arg( mSettings->creditURL() ) .arg( mSettings->creditName() ); else trailer += i18n("with %1").arg( mSettings->creditName() ); } *ts << "

" << trailer << "

\n"; } TQString HtmlExport::cleanChars(const TQString &text) { TQString txt = text; txt = txt.replace( "&", "&" ); txt = txt.replace( "<", "<" ); txt = txt.replace( ">", ">" ); txt = txt.replace( "\"", """ ); txt = txt.replace( TQString::fromUtf8("ä"), "ä" ); txt = txt.replace( TQString::fromUtf8("á"), "á" ); txt = txt.replace( TQString::fromUtf8("à"), "à" ); txt = txt.replace( TQString::fromUtf8("â"), "â" ); txt = txt.replace( TQString::fromUtf8("Ä"), "Ä" ); txt = txt.replace( TQString::fromUtf8("ó"), "ó" ); txt = txt.replace( TQString::fromUtf8("ô"), "ô" ); txt = txt.replace( TQString::fromUtf8("ö"), "ö" ); txt = txt.replace( TQString::fromUtf8("Ö"), "Ö" ); txt = txt.replace( TQString::fromUtf8("ü"), "ü" ); txt = txt.replace( TQString::fromUtf8("Ü"), "Ü" ); txt = txt.replace( TQString::fromUtf8("ß"), "ß" ); txt = txt.replace( TQString::fromUtf8("€"), "€" ); txt = txt.replace( TQString::fromUtf8("é"), "é" ); txt = txt.replace( TQString::fromUtf8("ë"), "ë" ); txt = txt.replace( TQString::fromUtf8("è"), "è" ); txt = txt.replace( TQString::fromUtf8("ñ"), "ñ" ); txt = txt.replace( TQString::fromUtf8("ç"), "ç" ); return txt; } TQString HtmlExport::styleSheet() const { if ( !mSettings->styleSheet().isEmpty() ) return mSettings->styleSheet(); TQString css; if ( TQApplication::reverseLayout() ) { 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 TQDate &date, const TQString &name) { if ( mHolidayMap[date].isEmpty() ) { mHolidayMap[date] = name; } else { mHolidayMap[date] = i18n("list of holidays", "%1, %2").arg(mHolidayMap[date]).arg(name); } } TQDate HtmlExport::fromDate() const { return mSettings->dateStart().date(); } TQDate HtmlExport::toDate() const { return mSettings->dateEnd().date(); }