diff --git a/kcal/htmlexport.cpp b/kcal/htmlexport.cpp
index db7f1fe79..e1d6d486a 100644
--- a/kcal/htmlexport.cpp
+++ b/kcal/htmlexport.cpp
@@ -1,781 +1,782 @@
/*
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 "incidenceformatter.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 << ""<" << endl;
*ts << "" << endl;
*ts << " "< " << endl;
if ( !d->mSettings->pageTitle().isEmpty() ) {
- *ts << " " << d->mSettings->pageTitle() << " "<" << d->mSettings->pageTitle() << "" << endl;
}
- *ts << " "<"<" << endl;
+ *ts << "" << endl;
// 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() << " "<" << d->mSettings->eventTitle() << "" << endl;
}
// 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() << " "<" << d->mSettings->todoListTitle() << "" << endl;
}
createTodoList( ts );
}
// Write Journals
if ( d->mSettings->journalView() ) {
if ( !d->mSettings->journalTitle().isEmpty() ) {
- *ts << "" << d->mSettings->journalTitle() << " "<" << d->mSettings->journalTitle() << "" << endl;
}
createJournalView( ts );
}
// Write Free/Busy
if ( d->mSettings->freeBusyView() ) {
if ( !d->mSettings->freeBusyTitle().isEmpty() ) {
- *ts << "" << d->mSettings->freeBusyTitle() << " "<" << d->mSettings->freeBusyTitle() << "" << endl;
}
createFreeBusyView( ts );
}
createFooter( ts );
// Write HTML trailer
- *ts << ""<" << endl;
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", hMon, hYear )
- << " "<" << endl;
if ( KGlobal::locale()->weekStartDay() == 1 ) {
start = start.addDays( 1 - start.dayOfWeek() );
} else {
if ( start.dayOfWeek() != 7 ) {
start = start.addDays( -start.dayOfWeek() );
}
}
- *ts << ""<" << endl;
// Write table header
*ts << " ";
for ( int i=0; i < 7; ++i ) {
*ts << "" << KGlobal::locale()->calendar()->weekDayName( start.addDays(i) ) << " ";
}
- *ts << " "<" << endl;
// Write days
while ( start <= end ) {
- *ts << " "<" << endl;
for ( int i=0; i < 7; ++i ) {
*ts << " ";
*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.constBegin(); it != events.constEnd(); ++it ) {
if ( checkSecrecy( *it ) ) {
createEvent( ts, *it, start, false );
}
}
*ts << "
";
} else {
*ts << " ";
}
- *ts << "
"<
" << endl;
start = start.addDays( 1 );
}
- *ts << " "<" << endl;
}
- *ts << ""<" << endl;
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 << ""<"<" << endl;
+ *ts << " " << endl;
*ts << " " << i18nc( "@title:column event start time",
- "Start Time" ) << " "<" << endl;
*ts << " " << i18nc( "@title:column event end time",
- "End Time" ) << " "<" << endl;
*ts << " " << i18nc( "@title:column event description",
- "Event" ) << " "<" << endl;
if ( d->mSettings->eventLocation() ) {
*ts << " " << i18nc( "@title:column event locatin",
- "Location" ) << " "<" << endl;
++columns;
}
if ( d->mSettings->eventCategories() ) {
*ts << " " << i18nc( "@title:column event categories",
- "Categories" ) << " "<" << endl;
++columns;
}
if ( d->mSettings->eventAttendees() ) {
*ts << " " << i18nc( "@title:column event attendees",
- "Attendees" ) << " "<" << endl;
++columns;
}
- *ts << " "<" << endl;
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 << " "
<< KGlobal::locale()->formatDate( dt )
- << " "<" << endl;
Event::List::ConstIterator it;
for ( it = events.constBegin(); it != events.constEnd(); ++it ) {
if ( checkSecrecy( *it ) ) {
createEvent( ts, *it, dt );
}
}
}
}
- *ts << "
"<" << endl;
}
void HtmlExport::createEvent ( QTextStream *ts, Event *event,
QDate date, bool withDescription )
{
kDebug() << event->summary();
- *ts << " "<" << endl;
if ( !event->allDay() ) {
if ( event->isMultiDay( d->mCalendar->timeSpec() ) && ( event->dtStart().date() != date ) ) {
- *ts << " "< " << endl;
} else {
*ts << " "
<< IncidenceFormatter::timeToString( event->dtStart(), true, d->mCalendar->timeSpec() )
- << " "<" << endl;
}
if ( event->isMultiDay( d->mCalendar->timeSpec() ) && ( event->dtEnd().date() != date ) ) {
- *ts << " "< " << endl;
} else {
*ts << " "
<< IncidenceFormatter::timeToString( event->dtEnd(), true, d->mCalendar->timeSpec() )
- << " "<" << endl;
}
} else {
- *ts << " "< " << endl;
}
- *ts << " "<" << cleanChars( event->summary() ) << ""<" << endl;
+ *ts << " " << cleanChars( event->summary() ) << " " << endl;
if ( withDescription && !event->description().isEmpty() ) {
- *ts << " " << breakString( cleanChars( event->description() ) ) << "
"<" << breakString( cleanChars( event->description() ) ) << "" << endl;
}
- *ts << " "<" << endl;
if ( d->mSettings->eventLocation() ) {
- *ts << " "<" << endl;
formatLocation( ts, event );
- *ts << " "<" << endl;
}
if ( d->mSettings->eventCategories() ) {
- *ts << " "<" << endl;
formatCategories( ts, event );
- *ts << " "<" << endl;
}
if ( d->mSettings->eventAttendees() ) {
- *ts << " "<" << endl;
formatAttendees( ts, event );
- *ts << " "<" << endl;
}
- *ts << " "<" << endl;
}
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.constBegin(); it != rawTodoList.constEnd(); ++it ) {
if ( (*it)->priority() == i && checkSecrecy( *it ) ) {
todoList.append( *it );
}
}
}
for ( it = rawTodoList.constBegin(); it != rawTodoList.constEnd(); ++it ) {
if ( (*it)->priority() == 0 && checkSecrecy( *it ) ) {
todoList.append( *it );
}
}
int columns = 3;
- *ts << ""<"<" << i18nc( "@title:column", "To-do" ) << ""<" << i18nc( "@title:column to-do priority", "Priority" ) << ""<" << i18nc( "@title:column to-do percent completed", "Completed" ) << ""<" << endl;
+ *ts << " " << endl;
+ *ts << " " << i18nc( "@title:column", "To-do" ) << " " << endl;
+ *ts << " " << i18nc( "@title:column to-do priority", "Priority" ) << " " << endl;
+ *ts << " " << i18nc( "@title:column to-do percent completed",
+ "Completed" ) << " " << endl;
if ( d->mSettings->taskDueDate() ) {
- *ts << " " << i18nc( "@title:column to-do due date", "Due Date" ) << " "<" << i18nc( "@title:column to-do due date", "Due Date" ) << "" << endl;
++columns;
}
if ( d->mSettings->taskLocation() ) {
- *ts << " " << i18nc( "@title:column to-do location", "Location" ) << " "<" << i18nc( "@title:column to-do location", "Location" ) << "" << endl;
++columns;
}
if ( d->mSettings->taskCategories() ) {
- *ts << " " << i18nc( "@title:column to-do categories", "Categories" ) << " "<" << i18nc( "@title:column to-do categories", "Categories" ) << "" << endl;
++columns;
}
if ( d->mSettings->taskAttendees() ) {
- *ts << " " << i18nc( "@title:column to-do attendees", "Attendees" ) << " "<" << i18nc( "@title:column to-do attendees", "Attendees" ) << "" << endl;
++columns;
}
- *ts << " "<" << endl;
// Create top-level list.
for ( it = todoList.constBegin(); it != todoList.constEnd(); ++it ) {
if ( !(*it)->relatedTo() ) {
createTodo( ts, *it );
}
}
// Create sub-level lists
for ( it = todoList.constBegin(); it != todoList.constEnd(); ++it ) {
Incidence::List relations = (*it)->relations();
if ( relations.count() ) {
// Generate sub-to-do list
- *ts << " "<" << endl;
*ts << " uid() << "\"> "
<< i18nc( "@title:column sub-to-dos of the parent to-do",
"Sub-To-dos of: " ) << "uid() << "\">" << cleanChars( (*it)->summary() )
- << " "<"<" << endl;
+ *ts << " " << endl;
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.constBegin(); it2 != relations.constEnd(); ++it2 ) {
Todo *ev3 = dynamic_cast( *it2 );
if ( ev3 && ev3->priority() == i ) {
sortedList.append( ev3 );
}
}
}
Incidence::List::ConstIterator it2;
for ( it2 = relations.constBegin(); it2 != relations.constEnd(); ++it2 ) {
Todo *ev3 = dynamic_cast( *it2 );
if ( ev3 && ev3->priority() == 0 ) {
sortedList.append( ev3 );
}
}
Todo::List::ConstIterator it3;
for ( it3 = sortedList.constBegin(); it3 != sortedList.constEnd(); ++it3 ) {
createTodo( ts, *it3 );
}
}
}
- *ts << "
"<" << endl;
}
void HtmlExport::createTodo( QTextStream *ts, Todo *todo )
{
kDebug();
bool completed = todo->isCompleted();
Incidence::List relations = todo->relations();
- *ts << ""<" << endl;
*ts << " "<uid() << "\">"<" << cleanChars( todo->summary() ) << ""<" << endl;
+ *ts << " uid() << "\"> " << endl;
+ *ts << " " << cleanChars( todo->summary() ) << " " << endl;
if ( !todo->description().isEmpty() ) {
- *ts << " " << breakString( cleanChars( todo->description() ) ) << "
"<" << breakString( cleanChars( todo->description() ) ) << "" << endl;
}
if ( relations.count() ) {
*ts << " "<" << endl;
}
- *ts << " "<" << endl;
*ts << " "<priority() <"<" << endl;
+ *ts << " " << todo->priority() << endl;
+ *ts << " " << endl;
*ts << " "<" << endl;
*ts << " " << i18nc( "@info/plain to-do percent complete",
- "%1 %", todo->percentComplete() ) <"<percentComplete() ) << endl;
+ *ts << " " << endl;
if ( d->mSettings->taskDueDate() ) {
*ts << " "<" << endl;
if ( todo->hasDueDate() ) {
- *ts << " " << IncidenceFormatter::dateToString(todo->dtDue(true)) <dtDue( true ) ) << endl;
} else {
- *ts << " "<"<" << endl;
}
if ( d->mSettings->taskLocation() ) {
*ts << " "<" << endl;
formatLocation( ts, todo );
- *ts << " "<" << endl;
}
if ( d->mSettings->taskCategories() ) {
*ts << " "<" << endl;
formatCategories( ts, todo );
- *ts << " "<" << endl;
}
if ( d->mSettings->taskAttendees() ) {
*ts << " "<" << endl;
formatAttendees( ts, todo );
- *ts << " "<" << endl;
}
- *ts << " "<" << endl;
}
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() ) <location() ) << endl;
} else {
- *ts << " "<categoriesStr().isEmpty() ) {
- *ts << " " << cleanChars( incidence->categoriesStr() ) <categoriesStr() ) << endl;
} else {
- *ts << " "<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() ) << " "<" << endl;
} else {
*ts << incidence->organizer().fullName();
}
}
#else
*ts << incidence->organizer().fullName();
#endif
*ts << " ";
Attendee::List::ConstIterator it;
for ( it = attendees.constBegin(); it != attendees.constEnd(); ++it ) {
Attendee *a = *it;
if ( !a->email().isEmpty() ) {
*ts << "email();
*ts << "\">" << cleanChars( a->name() ) << " ";
} else {
*ts << " " << cleanChars( a->name() );
}
- *ts << " " < " << endl;
}
} else {
- *ts << " "< ";
}
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 << "
"<" << trailer << "
" << endl;
}
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();
}
diff --git a/kcal/scheduler.cpp b/kcal/scheduler.cpp
index 7c6016ba1..39f81048c 100644
--- a/kcal/scheduler.cpp
+++ b/kcal/scheduler.cpp
@@ -1,597 +1,597 @@
/*
This file is part of the kcal library.
Copyright (c) 2001,2004 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 "scheduler.h"
#include "calendar.h"
#include "event.h"
#include "todo.h"
#include "freebusy.h"
#include "freebusycache.h"
#include "icalformat.h"
#include
#include
#include
#include
using namespace KCal;
//@cond PRIVATE
class KCal::ScheduleMessage::Private
{
public:
Private() {}
IncidenceBase *mIncidence;
iTIPMethod mMethod;
Status mStatus;
QString mError;
};
//@endcond
ScheduleMessage::ScheduleMessage( IncidenceBase *incidence,
iTIPMethod method,
ScheduleMessage::Status status )
: d( new KCal::ScheduleMessage::Private )
{
d->mIncidence = incidence;
d->mMethod = method;
d->mStatus = status;
}
ScheduleMessage::~ScheduleMessage()
{
delete d;
}
IncidenceBase *ScheduleMessage::event()
{
return d->mIncidence;
}
iTIPMethod ScheduleMessage::method()
{
return d->mMethod;
}
ScheduleMessage::Status ScheduleMessage::status()
{
return d->mStatus;
}
QString ScheduleMessage::statusName( ScheduleMessage::Status status )
{
switch( status ) {
case PublishNew:
return i18nc( "@item new message posting", "New Message Publish" );
case PublishUpdate:
return i18nc( "@item updated message", "Updated Message Published" );
case Obsolete:
return i18nc( "@item obsolete status", "Obsolete" );
case RequestNew:
return i18nc( "@item request new message posting", "Request New Message" );
case RequestUpdate:
return i18nc( "@item request updated posting", "Request Updated Message" );
default:
return i18nc( "@item unknown status", "Unknown Status: %1", status );
}
}
QString ScheduleMessage::error()
{
return d->mError;
}
//@cond PRIVATE
struct KCal::Scheduler::Private
{
Private()
: mFreeBusyCache( 0 )
{
}
FreeBusyCache *mFreeBusyCache;
};
//@endcond
Scheduler::Scheduler( Calendar *calendar ) : d( new KCal::Scheduler::Private )
{
mCalendar = calendar;
mFormat = new ICalFormat();
mFormat->setTimeSpec( calendar->timeSpec() );
}
Scheduler::~Scheduler()
{
delete mFormat;
delete d;
}
void Scheduler::setFreeBusyCache( FreeBusyCache *c )
{
d->mFreeBusyCache = c;
}
FreeBusyCache *Scheduler::freeBusyCache() const
{
return d->mFreeBusyCache;
}
bool Scheduler::acceptTransaction( IncidenceBase *incidence,
iTIPMethod method,
ScheduleMessage::Status status )
{
return acceptTransaction( incidence, method, status, QString() );
}
bool Scheduler::acceptTransaction( IncidenceBase *incidence,
iTIPMethod method,
ScheduleMessage::Status status,
const QString &email )
{
kDebug() << "method=" << methodName( method );
switch ( method ) {
case iTIPPublish:
return acceptPublish( incidence, status, method );
case iTIPRequest:
return acceptRequest( incidence, status, email );
case iTIPAdd:
return acceptAdd( incidence, status );
case iTIPCancel:
return acceptCancel( incidence, status );
case iTIPDeclineCounter:
return acceptDeclineCounter( incidence, status );
case iTIPReply:
return acceptReply( incidence, status, method );
case iTIPRefresh:
return acceptRefresh( incidence, status );
case iTIPCounter:
return acceptCounter( incidence, status );
default:
break;
}
deleteTransaction( incidence );
return false;
}
QString Scheduler::methodName( iTIPMethod method )
{
switch ( method ) {
case iTIPPublish:
return QLatin1String( "Publish" );
case iTIPRequest:
return QLatin1String( "Request" );
case iTIPRefresh:
return QLatin1String( "Refresh" );
case iTIPCancel:
return QLatin1String( "Cancel" );
case iTIPAdd:
return QLatin1String( "Add" );
case iTIPReply:
return QLatin1String( "Reply" );
case iTIPCounter:
return QLatin1String( "Counter" );
case iTIPDeclineCounter:
return QLatin1String( "Decline Counter" );
default:
return QLatin1String( "Unknown" );
}
}
QString Scheduler::translatedMethodName( iTIPMethod method )
{
switch ( method ) {
case iTIPPublish:
return i18nc( "@item event, to-do, journal or freebusy posting", "Publish" );
case iTIPRequest:
return i18nc( "@item event, to-do or freebusy scheduling requests", "Request" );
case iTIPReply:
return i18nc( "@item event, to-do or freebusy reply to request", "Reply" );
case iTIPAdd:
return i18nc(
"@item event, to-do or journal additional property request", "Add" );
case iTIPCancel:
return i18nc( "@item event, to-do or journal cancellation notice", "Cancel" );
case iTIPRefresh:
return i18nc( "@item event or to-do description update request", "Refresh" );
case iTIPCounter:
return i18nc( "@item event or to-do submit counter proposal", "Counter" );
case iTIPDeclineCounter:
return i18nc( "@item event or to-do decline a counter proposal", "Decline Counter" );
default:
return i18nc( "@item no method", "Unknown" );
}
}
bool Scheduler::deleteTransaction(IncidenceBase *)
{
return true;
}
bool Scheduler::acceptPublish( IncidenceBase *newIncBase,
ScheduleMessage::Status status,
iTIPMethod method )
{
if( newIncBase->type() == "FreeBusy" ) {
return acceptFreeBusy( newIncBase, method );
}
bool res = false;
kDebug() << "status=" << ScheduleMessage::statusName( status );
Incidence *newInc = static_cast( newIncBase );
Incidence *calInc = mCalendar->incidence( newIncBase->uid() );
switch ( status ) {
case ScheduleMessage::Unknown:
case ScheduleMessage::PublishNew:
case ScheduleMessage::PublishUpdate:
res = true;
if ( calInc ) {
if ( ( newInc->revision() > calInc->revision() ) ||
( newInc->revision() == calInc->revision() &&
newInc->lastModified() > calInc->lastModified() ) ) {
mCalendar->deleteIncidence( calInc );
} else {
res = false;
}
}
if ( res ) {
mCalendar->addIncidence( newInc );
}
break;
case ScheduleMessage::Obsolete:
res = true;
break;
default:
break;
}
deleteTransaction( newIncBase );
return res;
}
bool Scheduler::acceptRequest( IncidenceBase *incidence,
ScheduleMessage::Status status )
{
return acceptRequest( incidence, status, QString() );
}
bool Scheduler::acceptRequest( IncidenceBase *incidence,
ScheduleMessage::Status status,
const QString &email )
{
Incidence *inc = static_cast( incidence );
if ( !inc ) {
return false;
}
- if ( inc->type() == "FreeBusy" ) {
+ if ( inc->type() == "FreeBusy" ) {
// reply to this request is handled in korganizer's incomingdialog
return true;
}
const Incidence::List existingIncidences = mCalendar->incidencesFromSchedulingID( inc->uid() );
kDebug() << "status=" << ScheduleMessage::statusName( status )
<< ": found " << existingIncidences.count()
<< " incidences with schedulingID " << inc->schedulingID();
Incidence::List::ConstIterator incit = existingIncidences.begin();
for ( ; incit != existingIncidences.end() ; ++incit ) {
Incidence *i = *incit;
kDebug() << "Considering this found event ("
<< ( i->isReadOnly() ? "readonly" : "readwrite" )
<< ") :" << mFormat->toString( i );
// If it's readonly, we can't possible update it.
if ( i->isReadOnly() ) {
continue;
}
if ( i->revision() <= inc->revision() ) {
// The new incidence might be an update for the found one
bool isUpdate = true;
// Code for new invitations:
// If you think we could check the value of "status" to be RequestNew: we can't.
// It comes from a similar check inside libical, where the event is compared to
// other events in the calendar. But if we have another version of the event around
// (e.g. shared folder for a group), the status could be RequestNew, Obsolete or Updated.
kDebug() << "looking in " << i->uid() << "'s attendees";
// This is supposed to be a new request, not an update - however we want to update
// the existing one to handle the "clicking more than once on the invitation" case.
// So check the attendee status of the attendee.
const KCal::Attendee::List attendees = i->attendees();
KCal::Attendee::List::ConstIterator ait;
for ( ait = attendees.begin(); ait != attendees.end(); ++ait ) {
if( (*ait)->email() == email && (*ait)->status() == Attendee::NeedsAction ) {
// This incidence wasn't created by me - it's probably in a shared folder
// and meant for someone else, ignore it.
kDebug() << "ignoring " << i->uid() << " since I'm still NeedsAction there";
isUpdate = false;
break;
}
}
if ( isUpdate ) {
if ( i->revision() == inc->revision() &&
i->lastModified() > inc->lastModified() ) {
// This isn't an update - the found incidence was modified more recently
kDebug() << "This isn't an update - the found incidence was modified more recently";
deleteTransaction( i );
return false;
}
kDebug() << "replacing existing incidence " << i->uid();
mCalendar->deleteIncidence( i );
break; // replacing one is enough
}
} else {
// This isn't an update - the found incidence has a bigger revision number
kDebug() << "This isn't an update - the found incidence has a bigger revision number";
deleteTransaction(incidence);
return false;
}
}
// Move the uid to be the schedulingID and make a unique UID
inc->setSchedulingID( inc->uid() );
inc->setUid( CalFormat::createUniqueId() );
// in case this is an update and we didn't find the to-be-updated incidence,
// ask whether we should create a new one, or drop the update
if ( existingIncidences.count() > 0 || inc->revision() == 0 ||
KMessageBox::warningYesNo(
0,
i18nc( "@info",
"The event, to-do or journal to be updated could not be found. "
"Maybe it has already been deleted, or the calendar that "
"contains it is disabled. Press 'Store' to create a new "
"one or 'Throw away' to discard this update." ),
i18nc( "@title", "Discard this update?" ),
KGuiItem( i18nc( "@option", "Store" ) ),
KGuiItem( i18nc( "@option", "Throw away" ) ) ) == KMessageBox::Yes ) {
kDebug() << "Storing new incidence with scheduling uid=" << inc->schedulingID()
<< " and uid=" << inc->uid();
mCalendar->addIncidence( inc );
}
deleteTransaction(incidence);
return true;
}
bool Scheduler::acceptAdd( IncidenceBase *incidence, ScheduleMessage::Status /* status */)
{
deleteTransaction(incidence);
return false;
}
bool Scheduler::acceptCancel( IncidenceBase *incidence, ScheduleMessage::Status /* status */)
{
const IncidenceBase *toDelete = mCalendar->incidenceFromSchedulingID( incidence->uid() );
bool ret = true;
if ( toDelete ) {
if ( toDelete->type() == "Event" ) {
Event *event = mCalendar->event( toDelete->uid() );
ret = ( event && mCalendar->deleteEvent( event ) );
} else if ( toDelete->type() == "Todo" ) {
Todo *todo = mCalendar->todo( toDelete->uid() );
ret = ( todo && mCalendar->deleteTodo( todo ) );
}
}
if ( !ret ) {
KMessageBox::error(
0,
i18n( "The event or task to be canceled could not be removed from your calendar. "
"Maybe it has already been deleted, or the calendar that "
"contains it is disabled." ) );
}
deleteTransaction(incidence);
return ret;
}
bool Scheduler::acceptDeclineCounter( IncidenceBase *incidence,
ScheduleMessage::Status status )
{
Q_UNUSED( status );
deleteTransaction( incidence );
return false;
}
bool Scheduler::acceptReply( IncidenceBase *incidence,
ScheduleMessage::Status status,
iTIPMethod method )
{
Q_UNUSED( status );
if ( incidence->type() == "FreeBusy" ) {
return acceptFreeBusy( incidence, method );
}
bool ret = false;
Event *ev = mCalendar->event( incidence->uid() );
Todo *to = mCalendar->todo( incidence->uid() );
// try harder to find the correct incidence
if ( !ev && !to ) {
const Incidence::List list = mCalendar->incidences();
for ( Incidence::List::ConstIterator it=list.constBegin(), end=list.constEnd();
it != end; ++it ) {
if ( (*it)->schedulingID() == incidence->uid() ) {
ev = dynamic_cast( *it );
to = dynamic_cast( *it );
break;
}
}
}
if ( ev || to ) {
//get matching attendee in calendar
kDebug() << "match found!";
Attendee::List attendeesIn = incidence->attendees();
Attendee::List attendeesEv;
Attendee::List attendeesNew;
if ( ev ) {
attendeesEv = ev->attendees();
}
if ( to ) {
attendeesEv = to->attendees();
}
Attendee::List::ConstIterator inIt;
Attendee::List::ConstIterator evIt;
for ( inIt = attendeesIn.constBegin(); inIt != attendeesIn.constEnd(); ++inIt ) {
Attendee *attIn = *inIt;
bool found = false;
for ( evIt = attendeesEv.constBegin(); evIt != attendeesEv.constEnd(); ++evIt ) {
Attendee *attEv = *evIt;
if ( attIn->email().toLower() == attEv->email().toLower() ) {
//update attendee-info
kDebug() << "update attendee";
attEv->setStatus( attIn->status() );
attEv->setDelegate( attIn->delegate() );
attEv->setDelegator( attIn->delegator() );
ret = true;
found = true;
}
}
if ( !found && attIn->status() != Attendee::Declined ) {
attendeesNew.append( attIn );
}
}
bool attendeeAdded = false;
for ( Attendee::List::ConstIterator it = attendeesNew.constBegin();
it != attendeesNew.constEnd(); ++it ) {
Attendee *attNew = *it;
QString msg =
i18nc( "@info", "%1 wants to attend %2 but was not invited.",
attNew->fullName(),
( ev ? ev->summary() : to->summary() ) );
if ( !attNew->delegator().isEmpty() ) {
msg = i18nc( "@info", "%1 wants to attend %2 on behalf of %3.",
attNew->fullName(),
( ev ? ev->summary() : to->summary() ), attNew->delegator() );
}
if ( KMessageBox::questionYesNo(
0, msg, i18nc( "@title", "Uninvited attendee" ),
KGuiItem( i18nc( "@option", "Accept Attendance" ) ),
KGuiItem( i18nc( "@option", "Reject Attendance" ) ) ) != KMessageBox::Yes ) {
KCal::Incidence *cancel = dynamic_cast( incidence );
if ( cancel ) {
cancel->addComment(
i18nc( "@info",
"The organizer rejected your attendance at this meeting." ) );
}
performTransaction( cancel ? cancel : incidence, iTIPCancel, attNew->fullName() );
// ### can't delete cancel here because it is aliased to incidence which
// is accessed in the next loop iteration (CID 4232)
// delete cancel;
continue;
}
Attendee *a = new Attendee( attNew->name(), attNew->email(), attNew->RSVP(),
attNew->status(), attNew->role(), attNew->uid() );
a->setDelegate( attNew->delegate() );
a->setDelegator( attNew->delegator() );
if ( ev ) {
ev->addAttendee( a );
} else if ( to ) {
to->addAttendee( a );
}
ret = true;
attendeeAdded = true;
}
// send update about new participants
if ( attendeeAdded ) {
if ( ev ) {
ev->setRevision( ev->revision() + 1 );
performTransaction( ev, iTIPRequest );
}
if ( to ) {
to->setRevision( to->revision() + 1 );
performTransaction( to, iTIPRequest );
}
}
if ( ret ) {
// We set at least one of the attendees, so the incidence changed
// Note: This should not result in a sequence number bump
if ( ev ) {
ev->updated();
} else if ( to ) {
to->updated();
}
}
if ( to ) {
// for VTODO a REPLY can be used to update the completion status of
// a to-do. see RFC2446 3.4.3
Todo *update = dynamic_cast ( incidence );
Q_ASSERT( update );
if ( update && ( to->percentComplete() != update->percentComplete() ) ) {
to->setPercentComplete( update->percentComplete() );
to->updated();
}
}
} else {
kError(5800) << "No incidence for scheduling\n";
}
if ( ret ) {
deleteTransaction( incidence );
}
return ret;
}
bool Scheduler::acceptRefresh( IncidenceBase *incidence, ScheduleMessage::Status status )
{
Q_UNUSED( status );
// handled in korganizer's IncomingDialog
deleteTransaction( incidence );
return false;
}
bool Scheduler::acceptCounter( IncidenceBase *incidence, ScheduleMessage::Status status )
{
Q_UNUSED( status );
deleteTransaction( incidence );
return false;
}
bool Scheduler::acceptFreeBusy( IncidenceBase *incidence, iTIPMethod method )
{
if ( !d->mFreeBusyCache ) {
kError() << "KCal::Scheduler: no FreeBusyCache.";
return false;
}
FreeBusy *freebusy = static_cast(incidence);
kDebug() << "freeBusyDirName:" << freeBusyDir();
Person from;
if( method == iTIPPublish ) {
from = freebusy->organizer();
}
if ( ( method == iTIPReply ) && ( freebusy->attendeeCount() == 1 ) ) {
Attendee *attendee = freebusy->attendees().first();
from.setName( attendee->name() );
from.setEmail( attendee->email() );
}
if ( !d->mFreeBusyCache->saveFreeBusy( freebusy, from ) ) {
return false;
}
deleteTransaction( incidence );
return true;
}