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 | |
35 | typedef boost::shared_ptr<KCal::Incidence> IncidencePtr; |
36 | |
37 | using namespace Akonadi; |
38 | |
39 | //// ItemSerializerPlugin interface |
40 | |
41 | bool 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 | |
60 | void 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 | |
75 | static 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 | |
83 | static QString toString( KCal::Attendee *attendee ) |
84 | { |
85 | return attendee->name() + QLatin1Char( '<' ) + attendee->email() + QLatin1Char( '>' ); |
86 | } |
87 | |
88 | static QString toString( KCal::Alarm * ) |
89 | { |
90 | return QString(); |
91 | } |
92 | |
93 | /* |
94 | static QString toString( KCal::Incidence * ) |
95 | { |
96 | return QString(); |
97 | } |
98 | */ |
99 | |
100 | static QString toString( KCal::Attachment * ) |
101 | { |
102 | return QString(); |
103 | } |
104 | |
105 | static QString toString( const QDate &date ) |
106 | { |
107 | return date.toString(); |
108 | } |
109 | |
110 | static QString toString( const KDateTime &dateTime ) |
111 | { |
112 | return dateTime.dateTime().toString(); |
113 | } |
114 | |
115 | static QString toString( const QString &str ) |
116 | { |
117 | return str; |
118 | } |
119 | |
120 | static QString toString( bool value ) |
121 | { |
122 | if ( value ) |
123 | return i18n( "Yes" ); |
124 | else |
125 | return i18n( "No" ); |
126 | } |
127 | |
128 | template <class T> |
129 | static 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 | |
145 | static 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 | |
172 | static 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 | |
217 | static 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 | |
237 | static 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 | |
266 | void 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 | |
301 | Q_EXPORT_PLUGIN2( akonadi_serializer_kcal, SerializerPluginKCal ) |
302 | |
303 | |