1/*
2 Copyright (c) 2007 Volker Krause <vkrause@kde.org>
3
4 This library is free software; you can redistribute it and/or modify it
5 under the terms of the GNU Library General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or (at your
7 option) any later version.
8
9 This library is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12 License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to the
16 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 02110-1301, USA.
18*/
19
20#include "akonadi_serializer_kcal.h"
21
22#include <akonadi/abstractdifferencesreporter.h>
23#include <akonadi/item.h>
24
25#include <KCal/Event>
26#include <KCal/Todo>
27
28#include <kdebug.h>
29#include <klocale.h>
30
31#include <QtCore/qplugin.h>
32
33#include <boost/shared_ptr.hpp>
34
35typedef boost::shared_ptr<KCal::Incidence> IncidencePtr;
36
37using namespace Akonadi;
38
39//// ItemSerializerPlugin interface
40
41bool SerializerPluginKCal::deserialize(Item & item, const QByteArray & label, QIODevice & data, int version)
42{
43 Q_UNUSED( version );
44
45 if ( label != Item::FullPayload ) {
46 return false;
47 }
48
49 KCal::Incidence* i = mFormat.fromString( QString::fromUtf8( data.readAll() ) );
50 if ( !i ) {
51 kWarning( 5263 ) << "Failed to parse incidence!";
52 data.seek( 0 );
53 kWarning( 5263 ) << QString::fromUtf8( data.readAll() );
54 return false;
55 }
56 item.setPayload<IncidencePtr>( IncidencePtr( i ) );
57 return true;
58}
59
60void SerializerPluginKCal::serialize(const Item & item, const QByteArray & label, QIODevice & data, int &version)
61{
62 Q_UNUSED( version );
63
64 if ( label != Item::FullPayload || !item.hasPayload<IncidencePtr>() )
65 return;
66 IncidencePtr i = item.payload<IncidencePtr>();
67 // ### I guess this can be done without hardcoding stuff
68 data.write( "BEGIN:VCALENDAR\nPRODID:-//K Desktop Environment//NONSGML libkcal 3.2//EN\nVERSION:2.0\n" );
69 data.write( mFormat.toString( i.get() ).toUtf8() );
70 data.write( "\nEND:VCALENDAR" );
71}
72
73//// DifferencesAlgorithmInterface
74
75static bool compareString( const QString &left, const QString &right )
76{
77 if ( left.isEmpty() && right.isEmpty() )
78 return true;
79 else
80 return left == right;
81}
82
83static QString toString( KCal::Attendee *attendee )
84{
85 return attendee->name() + QLatin1Char( '<' ) + attendee->email() + QLatin1Char( '>' );
86}
87
88static QString toString( KCal::Alarm * )
89{
90 return QString();
91}
92
93/*
94static QString toString( KCal::Incidence * )
95{
96 return QString();
97}
98*/
99
100static QString toString( KCal::Attachment * )
101{
102 return QString();
103}
104
105static QString toString( const QDate &date )
106{
107 return date.toString();
108}
109
110static QString toString( const KDateTime &dateTime )
111{
112 return dateTime.dateTime().toString();
113}
114
115static QString toString( const QString &str )
116{
117 return str;
118}
119
120static QString toString( bool value )
121{
122 if ( value )
123 return i18n( "Yes" );
124 else
125 return i18n( "No" );
126}
127
128template <class T>
129static void compareList( AbstractDifferencesReporter *reporter,
130 const QString &id,
131 const QList<T> &left,
132 const QList<T> &right )
133{
134 for ( int i = 0; i < left.count(); ++i ) {
135 if ( !right.contains( left[ i ] ) )
136 reporter->addProperty( AbstractDifferencesReporter::AdditionalLeftMode, id, toString( left[ i ] ), QString() );
137 }
138
139 for ( int i = 0; i < right.count(); ++i ) {
140 if ( !left.contains( right[ i ] ) )
141 reporter->addProperty( AbstractDifferencesReporter::AdditionalRightMode, id, QString(), toString( right[ i ] ) );
142 }
143}
144
145static void compareIncidenceBase( AbstractDifferencesReporter *reporter,
146 const KCal::IncidenceBase *left,
147 const KCal::IncidenceBase *right )
148{
149 compareList( reporter, i18n( "Attendees" ), left->attendees(), right->attendees() );
150
151 if ( !compareString( left->organizer().fullName(), right->organizer().fullName() ) )
152 reporter->addProperty( AbstractDifferencesReporter::ConflictMode, i18n( "Organizer" ),
153 left->organizer().fullName(), right->organizer().fullName() );
154
155 if ( !compareString( left->uid(), right->uid() ) )
156 reporter->addProperty( AbstractDifferencesReporter::ConflictMode, i18n( "UID" ),
157 left->uid(), right->uid() );
158
159 if ( left->allDay() != right->allDay() )
160 reporter->addProperty( AbstractDifferencesReporter::ConflictMode, i18n( "Is all-day" ),
161 toString( left->allDay() ), toString( right->allDay() ) );
162
163 if ( left->hasDuration() != right->hasDuration() )
164 reporter->addProperty( AbstractDifferencesReporter::ConflictMode, i18n( "Has duration" ),
165 toString( left->hasDuration() ), toString( right->hasDuration() ) );
166
167 if ( left->duration() != right->duration() )
168 reporter->addProperty( AbstractDifferencesReporter::ConflictMode, i18n( "Duration" ),
169 QString::number( left->duration().asSeconds() ), QString::number( right->duration().asSeconds() ) );
170}
171
172static void compareIncidence( AbstractDifferencesReporter *reporter,
173 const KCal::Incidence *left,
174 const KCal::Incidence *right )
175{
176 if ( !compareString( left->description(), right->description() ) )
177 reporter->addProperty( AbstractDifferencesReporter::ConflictMode, i18n( "Description" ),
178 left->description(), right->description() );
179
180 if ( !compareString( left->summary(), right->summary() ) )
181 reporter->addProperty( AbstractDifferencesReporter::ConflictMode, i18n( "Summary" ),
182 left->summary(), right->summary() );
183
184 if ( left->status() != right->status() )
185 reporter->addProperty( AbstractDifferencesReporter::ConflictMode, i18n( "Status" ),
186 left->statusStr(), right->statusStr() );
187
188 if ( left->secrecy() != right->secrecy() )
189 reporter->addProperty( AbstractDifferencesReporter::ConflictMode, i18n( "Secrecy" ),
190 toString( left->secrecy() ), toString( right->secrecy() ) );
191
192 if ( left->priority() != right->priority() )
193 reporter->addProperty( AbstractDifferencesReporter::ConflictMode, i18n( "Priority" ),
194 toString( left->priority() ), toString( right->priority() ) );
195
196 if ( !compareString( left->location(), right->location() ) )
197 reporter->addProperty( AbstractDifferencesReporter::ConflictMode, i18n( "Location" ),
198 left->location(), right->location() );
199
200 compareList( reporter, i18n( "Categories" ), left->categories(), right->categories() );
201 compareList( reporter, i18n( "Alarms" ), left->alarms(), right->alarms() );
202 compareList( reporter, i18n( "Resources" ), left->resources(), right->resources() );
203 compareList( reporter, i18n( "Attachments" ), left->attachments(), right->attachments() );
204 compareList( reporter, i18n( "Exception Dates" ), left->recurrence()->exDates(), right->recurrence()->exDates() );
205 compareList( reporter, i18n( "Exception Times" ), left->recurrence()->exDateTimes(), right->recurrence()->exDateTimes() );
206 // TODO: recurrence dates and date/times, exrules, rrules
207
208 if ( left->created() != right->created() )
209 reporter->addProperty( AbstractDifferencesReporter::ConflictMode,
210 i18n( "Created" ), left->created().toString(), right->created().toString() );
211
212 if ( !compareString( left->relatedToUid(), right->relatedToUid() ) )
213 reporter->addProperty( AbstractDifferencesReporter::ConflictMode,
214 i18n( "Related Uid" ), left->relatedToUid(), right->relatedToUid() );
215}
216
217static void compareEvent( AbstractDifferencesReporter *reporter,
218 const KCal::Event *left,
219 const KCal::Event *right )
220{
221
222 if ( left->dtStart() != right->dtStart() )
223 reporter->addProperty( AbstractDifferencesReporter::ConflictMode, i18n( "Start time" ),
224 left->dtStart().toString(), right->dtStart().toString() );
225
226 if ( left->hasEndDate() != right->hasEndDate() )
227 reporter->addProperty( AbstractDifferencesReporter::ConflictMode, i18n( "Has End Date" ),
228 toString( left->hasEndDate() ), toString( right->hasEndDate() ) );
229
230 if ( left->dtEnd() != right->dtEnd() )
231 reporter->addProperty( AbstractDifferencesReporter::ConflictMode, i18n( "End Date" ),
232 left->dtEnd().toString(), right->dtEnd().toString() );
233
234 // TODO: check transparency
235}
236
237static void compareTodo( AbstractDifferencesReporter *reporter,
238 const KCal::Todo *left,
239 const KCal::Todo *right )
240{
241 if ( left->hasStartDate() != right->hasStartDate() )
242 reporter->addProperty( AbstractDifferencesReporter::ConflictMode, i18n( "Has Start Date" ),
243 toString( left->hasStartDate() ), toString( right->hasStartDate() ) );
244
245 if ( left->hasDueDate() != right->hasDueDate() )
246 reporter->addProperty( AbstractDifferencesReporter::ConflictMode, i18n( "Has Due Date" ),
247 toString( left->hasDueDate() ), toString( right->hasDueDate() ) );
248
249 if ( left->dtDue() != right->dtDue() )
250 reporter->addProperty( AbstractDifferencesReporter::ConflictMode, i18n( "Due Date" ),
251 left->dtDue().toString(), right->dtDue().toString() );
252
253 if ( left->hasCompletedDate() != right->hasCompletedDate() )
254 reporter->addProperty( AbstractDifferencesReporter::ConflictMode, i18n( "Has Complete Date" ),
255 toString( left->hasCompletedDate() ), toString( right->hasCompletedDate() ) );
256
257 if ( left->percentComplete() != right->percentComplete() )
258 reporter->addProperty( AbstractDifferencesReporter::ConflictMode, i18n( "Complete" ),
259 QString::number( left->percentComplete() ), QString::number( right->percentComplete() ) );
260
261 if ( left->completed() != right->completed() )
262 reporter->addProperty( AbstractDifferencesReporter::ConflictMode, i18n( "Completed" ),
263 toString( left->completed() ), toString( right->completed() ) );
264}
265
266void SerializerPluginKCal::compare( Akonadi::AbstractDifferencesReporter *reporter,
267 const Akonadi::Item &leftItem,
268 const Akonadi::Item &rightItem )
269{
270 Q_ASSERT( reporter );
271 Q_ASSERT( leftItem.hasPayload<IncidencePtr>() );
272 Q_ASSERT( rightItem.hasPayload<IncidencePtr>() );
273
274 const IncidencePtr leftIncidencePtr = leftItem.payload<IncidencePtr>();
275 const IncidencePtr rightIncidencePtr = rightItem.payload<IncidencePtr>();
276
277 if ( leftIncidencePtr->type() == "Event" ) {
278 reporter->setLeftPropertyValueTitle( i18n( "Changed Event" ) );
279 reporter->setRightPropertyValueTitle( i18n( "Conflicting Event" ) );
280 } else if ( leftIncidencePtr->type() == "Todo" ) {
281 reporter->setLeftPropertyValueTitle( i18n( "Changed Todo" ) );
282 reporter->setRightPropertyValueTitle( i18n( "Conflicting Todo" ) );
283 }
284
285 compareIncidenceBase( reporter, leftIncidencePtr.get(), rightIncidencePtr.get() );
286 compareIncidence( reporter, leftIncidencePtr.get(), rightIncidencePtr.get() );
287
288 const KCal::Event *leftEvent = dynamic_cast<KCal::Event*>( leftIncidencePtr.get() );
289 const KCal::Event *rightEvent = dynamic_cast<KCal::Event*>( rightIncidencePtr.get() ) ;
290 if ( leftEvent && rightEvent ) {
291 compareEvent( reporter, leftEvent, rightEvent );
292 } else {
293 const KCal::Todo *leftTodo = dynamic_cast<KCal::Todo*>( leftIncidencePtr.get() );
294 const KCal::Todo *rightTodo = dynamic_cast<KCal::Todo*>( rightIncidencePtr.get() );
295 if ( leftTodo && rightTodo ) {
296 compareTodo( reporter, leftTodo, rightTodo );
297 }
298 }
299}
300
301Q_EXPORT_PLUGIN2( akonadi_serializer_kcal, SerializerPluginKCal )
302
303