1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2015 The Qt Company Ltd. |
4 | ** Contact: http://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtVersit module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL21$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see http://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at http://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 2.1 or version 3 as published by the Free |
20 | ** Software Foundation and appearing in the file LICENSE.LGPLv21 and |
21 | ** LICENSE.LGPLv3 included in the packaging of this file. Please review the |
22 | ** following information to ensure the GNU Lesser General Public License |
23 | ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and |
24 | ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
25 | ** |
26 | ** As a special exception, The Qt Company gives you certain additional |
27 | ** rights. These rights are described in The Qt Company LGPL Exception |
28 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
29 | ** |
30 | ** $QT_END_LICENSE$ |
31 | ** |
32 | ****************************************************************************/ |
33 | |
34 | #include "qversitcontactexporter.h" |
35 | #include "qversitcontactexporter_p.h" |
36 | |
37 | #include <QtCore/qdebug.h> |
38 | #include <QtCore/qurl.h> |
39 | |
40 | #include <QtContacts/qcontactdetail.h> |
41 | |
42 | #include "qversitcontactimporter_p.h" |
43 | #include "qversitcontactsdefs_p.h" |
44 | #include "qversitutils_p.h" |
45 | #include "qversitcontacthandler.h" |
46 | #include "qversitcontactpluginloader_p.h" |
47 | |
48 | QTCONTACTS_USE_NAMESPACE |
49 | |
50 | QT_BEGIN_NAMESPACE_VERSIT |
51 | |
52 | /*! |
53 | * Constructor. |
54 | */ |
55 | QVersitContactExporterPrivate::QVersitContactExporterPrivate(const QStringList& profiles) : |
56 | mDetailHandler(NULL), |
57 | mDetailHandler2(NULL), |
58 | mDetailHandlerVersion(0), |
59 | mDefaultResourceHandler(new QVersitDefaultResourceHandler), |
60 | mResourceHandler(mDefaultResourceHandler) |
61 | { |
62 | // Detail mappings |
63 | int versitPropertyCount = |
64 | sizeof(versitContactDetailMappings)/sizeof(VersitContactDetailMapping); |
65 | // Put them in in reverse order so the entries at the top of the list take precedence |
66 | for (int i = versitPropertyCount-1; i >= 0; i--) { |
67 | mPropertyMappings.insert( |
68 | akey: versitContactDetailMappings[i].detailType, |
69 | avalue: QPair<int, QString>( |
70 | versitContactDetailMappings[i].detailField, |
71 | QLatin1String(versitContactDetailMappings[i].versitPropertyName))); |
72 | } |
73 | |
74 | // Contexts mappings |
75 | int contextCount = sizeof(versitContextMappings)/sizeof(VersitContextMapping); |
76 | for (int i=0; i < contextCount; i++) { |
77 | mContextMappings.insert( |
78 | akey: versitContextMappings[i].contactContext, |
79 | avalue: QLatin1String(versitContextMappings[i].versitString)); |
80 | } |
81 | |
82 | // Subtypes mappings |
83 | int subTypeCount = sizeof(versitSubTypeMappings)/sizeof(VersitSubTypeMapping); |
84 | for (int i=0; i < subTypeCount; i++) { |
85 | mSubTypeMappings.insert( |
86 | akey: QPair<QContactDetail::DetailType, int>( |
87 | versitSubTypeMappings[i].detailType, versitSubTypeMappings[i].contactSubType), |
88 | avalue: QLatin1String(versitSubTypeMappings[i].versitString)); |
89 | } |
90 | mPluginDetailHandlers = QVersitContactPluginLoader::instance()->createContactHandlers(profiles); |
91 | } |
92 | |
93 | /*! |
94 | * Destructor. |
95 | */ |
96 | QVersitContactExporterPrivate::~QVersitContactExporterPrivate() |
97 | { |
98 | delete mDefaultResourceHandler; |
99 | foreach (QVersitContactHandler* pluginHandler, mPluginDetailHandlers) { |
100 | delete pluginHandler; |
101 | } |
102 | } |
103 | |
104 | |
105 | /*! |
106 | * Export QT Contact into Versit Document. |
107 | */ |
108 | void QVersitContactExporterPrivate::exportContact( |
109 | const QContact& contact, |
110 | QVersitDocument& document) |
111 | { |
112 | QList<QContactDetail> allDetails = contact.details(); |
113 | foreach (const QContactDetail& detail, allDetails) { |
114 | if (mDetailHandler |
115 | && mDetailHandler->preProcessDetail(contact, detail, document: &document)) |
116 | continue; |
117 | |
118 | QList<QVersitProperty> removedProperties; |
119 | QList<QVersitProperty> generatedProperties; |
120 | QSet<int> processedFields; |
121 | |
122 | switch (detail.type()) { |
123 | case QContactDetail::TypeAddress: |
124 | encodeAddress(detail, generatedProperties: &generatedProperties, processedFields: &processedFields); |
125 | break; |
126 | case QContactDetail::TypeAnniversary: |
127 | encodeAnniversary(detail, generatedProperties: &generatedProperties, processedFields: &processedFields); |
128 | break; |
129 | case QContactDetail::TypeAvatar: |
130 | encodeAvatar(detail, generatedProperties: &generatedProperties, processedFields: &processedFields); |
131 | break; |
132 | case QContactDetail::TypeBirthday: |
133 | encodeBirthDay(detail, generatedProperties: &generatedProperties, processedFields: &processedFields); |
134 | break; |
135 | case QContactDetail::TypeDisplayLabel: |
136 | encodeDisplayLabel(detail, document, removedProperties: &removedProperties, generatedProperties: &generatedProperties, processedFields: &processedFields); |
137 | break; |
138 | case QContactDetail::TypeEmailAddress: |
139 | encodeEmail(detail, generatedProperties: &generatedProperties, processedFields: &processedFields); |
140 | break; |
141 | case QContactDetail::TypeExtendedDetail: |
142 | encodeExtendedDetail(detail, generatedProperties: &generatedProperties, processedFields: &processedFields); |
143 | break; |
144 | case QContactDetail::TypeFamily: |
145 | encodeFamily(detail, generatedProperties: &generatedProperties, processedFields: &processedFields); |
146 | break; |
147 | case QContactDetail::TypeFavorite: |
148 | encodeFavorite(detail, generatedProperties: &generatedProperties, processedFields: &processedFields); |
149 | break; |
150 | case QContactDetail::TypeGender: |
151 | encodeGender(detail, generatedProperties: &generatedProperties, processedFields: &processedFields); |
152 | break; |
153 | case QContactDetail::TypeGeoLocation: |
154 | encodeGeoLocation(detail, generatedProperties: &generatedProperties, processedFields: &processedFields); |
155 | break; |
156 | case QContactDetail::TypeGuid: |
157 | encodeUid(detail, generatedProperties: &generatedProperties, processedFields: &processedFields); |
158 | break; |
159 | case QContactDetail::TypeName: |
160 | encodeName(detail, document, removedProperties: &removedProperties, generatedProperties: &generatedProperties, processedFields: &processedFields); |
161 | break; |
162 | case QContactDetail::TypeNickname: |
163 | encodeNickname(detail, document, removedProperties: &removedProperties, generatedProperties: &generatedProperties, processedFields: &processedFields); |
164 | break; |
165 | case QContactDetail::TypeNote: |
166 | encodeNote(detail, generatedProperties: &generatedProperties, processedFields: &processedFields); |
167 | break; |
168 | case QContactDetail::TypeOnlineAccount: |
169 | encodeOnlineAccount(detail, generatedProperties: &generatedProperties, processedFields: &processedFields); |
170 | break; |
171 | case QContactDetail::TypeOrganization: |
172 | encodeOrganization(detail, generatedProperties: &generatedProperties, processedFields: &processedFields); |
173 | break; |
174 | case QContactDetail::TypePhoneNumber: |
175 | encodePhoneNumber(detail, generatedProperties: &generatedProperties, processedFields: &processedFields); |
176 | break; |
177 | case QContactDetail::TypeRingtone: |
178 | encodeRingtone(detail, generatedProperties: &generatedProperties, processedFields: &processedFields); |
179 | break; |
180 | case QContactDetail::TypeTag: |
181 | encodeTag(detail, document, removedProperties: &removedProperties, generatedProperties: &generatedProperties, processedFields: &processedFields); |
182 | break; |
183 | case QContactDetail::TypeTimestamp: |
184 | encodeRev(detail, generatedProperties: &generatedProperties, processedFields: &processedFields); |
185 | break; |
186 | case QContactDetail::TypeUrl: |
187 | encodeUrl(detail, generatedProperties: &generatedProperties, processedFields: &processedFields); |
188 | break; |
189 | case QContactDetail::TypeVersion: |
190 | encodeVersion(detail, generatedProperties: &generatedProperties, processedFields: &processedFields); |
191 | break; |
192 | default: |
193 | break; |
194 | } |
195 | |
196 | // run plugin handlers |
197 | foreach (QVersitContactExporterDetailHandlerV2* handler, mPluginDetailHandlers) { |
198 | handler->detailProcessed(contact, detail, document, |
199 | processedFields: &processedFields, toBeRemoved: &removedProperties, toBeAdded: &generatedProperties); |
200 | } |
201 | // run the v2 handler, if set |
202 | if (mDetailHandler2 && mDetailHandlerVersion > 1) { |
203 | mDetailHandler2->detailProcessed(contact, detail, document, |
204 | processedFields: &processedFields, toBeRemoved: &removedProperties, toBeAdded: &generatedProperties); |
205 | } |
206 | |
207 | foreach(const QVersitProperty& property, removedProperties) { |
208 | document.removeProperty(property); |
209 | } |
210 | foreach(const QVersitProperty& property, generatedProperties) { |
211 | document.addProperty(property); |
212 | } |
213 | |
214 | if (mDetailHandler && mDetailHandlerVersion == 1) { |
215 | mDetailHandler->postProcessDetail(contact, detail, alreadyProcessed: !processedFields.isEmpty(), document: &document); |
216 | } |
217 | } |
218 | |
219 | // run plugin handlers |
220 | foreach (QVersitContactExporterDetailHandlerV2* handler, mPluginDetailHandlers) { |
221 | handler->contactProcessed(contact, document: &document); |
222 | } |
223 | // run the v2 handler, if set |
224 | if (mDetailHandler2 && mDetailHandlerVersion > 1) { |
225 | mDetailHandler2->contactProcessed(contact, document: &document); |
226 | } |
227 | |
228 | ensureDocumentContainsName(document: &document); |
229 | return; |
230 | } |
231 | |
232 | /*! |
233 | * Adds to \a document an empty "N" property if it doesn't already have one. |
234 | */ |
235 | void QVersitContactExporterPrivate::ensureDocumentContainsName(QVersitDocument* document) |
236 | { |
237 | bool containsN = false; |
238 | foreach (const QVersitProperty& property, document->properties()) { |
239 | const QString& name = property.name(); |
240 | if (name == QStringLiteral("N" )) { |
241 | containsN = true; |
242 | } |
243 | } |
244 | |
245 | if (!containsN) { |
246 | QVersitProperty nProperty; |
247 | nProperty.setValueType(QVersitProperty::CompoundType); |
248 | nProperty.setName(QStringLiteral("N" )); |
249 | nProperty.setValue(QStringList() << QString() << QString() |
250 | << QString() << QString() << QString()); |
251 | document->addProperty(property: nProperty); |
252 | } |
253 | } |
254 | |
255 | /*! |
256 | * Encode Contact Name Field Information into the Versit Document |
257 | */ |
258 | void QVersitContactExporterPrivate::encodeName( |
259 | const QContactDetail& detail, |
260 | const QVersitDocument& document, |
261 | QList<QVersitProperty>* removedProperties, |
262 | QList<QVersitProperty>* generatedProperties, |
263 | QSet<int>* processedFields) |
264 | { |
265 | Q_UNUSED(document); |
266 | Q_UNUSED(removedProperties); |
267 | const QContactName &contactName = static_cast<const QContactName &>(detail); |
268 | if (!contactName.lastName().isEmpty() |
269 | || !contactName.firstName().isEmpty() |
270 | || !contactName.middleName().isEmpty() |
271 | || !contactName.prefix().isEmpty() |
272 | || !contactName.suffix().isEmpty()) { |
273 | QVersitProperty property; |
274 | property.setName(mPropertyMappings.value(akey: detail.type()).second); |
275 | property.setValue(QStringList() |
276 | << contactName.lastName() |
277 | << contactName.firstName() |
278 | << contactName.middleName() |
279 | << contactName.prefix() |
280 | << contactName.suffix()); |
281 | property.setValueType(QVersitProperty::CompoundType); |
282 | *generatedProperties << property; |
283 | } |
284 | |
285 | *processedFields << QContactName::FieldLastName |
286 | << QContactName::FieldFirstName |
287 | << QContactName::FieldMiddleName |
288 | << QContactName::FieldPrefix |
289 | << QContactName::FieldSuffix; |
290 | } |
291 | |
292 | /*! |
293 | * Encode Phone Number Field Information into the Versit Document |
294 | */ |
295 | void QVersitContactExporterPrivate::encodePhoneNumber( |
296 | const QContactDetail& detail, |
297 | QList<QVersitProperty>* generatedProperties, |
298 | QSet<int>* processedFields) |
299 | { |
300 | const QContactPhoneNumber &phoneNumber = static_cast<const QContactPhoneNumber &>(detail); |
301 | QList<int> subTypes = phoneNumber.subTypes(); |
302 | QList<int> phoneNumberContextInt; |
303 | |
304 | for (int i=0; i<phoneNumber.contexts().count(); i++) |
305 | phoneNumberContextInt << phoneNumber.contexts().at(i); |
306 | |
307 | QVersitProperty property; |
308 | if (subTypes.contains(t: QContactPhoneNumber::SubTypeAssistant)) |
309 | property.setName(QStringLiteral("X-ASSISTANT-TEL" )); |
310 | else |
311 | property.setName(QStringLiteral("TEL" )); |
312 | encodeParameters(property, detailType: detail.type(), contexts: phoneNumberContextInt, subTypes); |
313 | property.setValue(phoneNumber.number()); |
314 | *generatedProperties << property; |
315 | *processedFields << QContactPhoneNumber::FieldContext |
316 | << QContactPhoneNumber::FieldSubTypes |
317 | << QContactPhoneNumber::FieldNumber; |
318 | } |
319 | |
320 | /*! |
321 | * Encode Email Field Information into the Versit Document |
322 | */ |
323 | void QVersitContactExporterPrivate::encodeEmail( |
324 | const QContactDetail& detail, |
325 | QList<QVersitProperty>* generatedProperties, |
326 | QSet<int>* processedFields) |
327 | { |
328 | const QContactEmailAddress &emailAddress = static_cast<const QContactEmailAddress &>(detail); |
329 | QList<int> emailAddressContextInt; |
330 | |
331 | for (int i=0; i<emailAddress.contexts().count(); i++) |
332 | emailAddressContextInt << emailAddress.contexts().at(i); |
333 | |
334 | QVersitProperty property; |
335 | property.setName(mPropertyMappings.value(akey: detail.type()).second); |
336 | encodeParameters(property, detailType: detail.type(), contexts: emailAddressContextInt); |
337 | property.setValue(emailAddress.emailAddress()); |
338 | *generatedProperties << property; |
339 | *processedFields << QContactEmailAddress::FieldContext |
340 | << QContactEmailAddress::FieldEmailAddress; |
341 | } |
342 | |
343 | /*! |
344 | * Encode Address Field Information into the Versit Document |
345 | */ |
346 | void QVersitContactExporterPrivate::encodeAddress( |
347 | const QContactDetail& detail, |
348 | QList<QVersitProperty>* generatedProperties, |
349 | QSet<int>* processedFields) |
350 | { |
351 | const QContactAddress &address = static_cast<const QContactAddress &>(detail); |
352 | QList<int> addressContextInt; |
353 | |
354 | for (int i=0; i<address.contexts().count(); i++) |
355 | addressContextInt << address.contexts().at(i); |
356 | |
357 | QVersitProperty property; |
358 | property.setName(mPropertyMappings.value(akey: detail.type()).second); |
359 | encodeParameters(property, detailType: detail.type(), contexts: addressContextInt, subTypes: address.subTypes()); |
360 | property.setValue(QStringList() |
361 | << address.postOfficeBox() |
362 | << QString() // Leave out the extended address field |
363 | << address.street() |
364 | << address.locality() |
365 | << address.region() |
366 | << address.postcode() |
367 | << address.country()); |
368 | property.setValueType(QVersitProperty::CompoundType); |
369 | *generatedProperties << property; |
370 | *processedFields << QContactAddress::FieldContext |
371 | << QContactAddress::FieldSubTypes |
372 | << QContactAddress::FieldPostOfficeBox |
373 | << QContactAddress::FieldStreet |
374 | << QContactAddress::FieldLocality |
375 | << QContactAddress::FieldRegion |
376 | << QContactAddress::FieldPostcode |
377 | << QContactAddress::FieldCountry; |
378 | } |
379 | |
380 | /*! |
381 | * Encode URL Field Information into the Versit Document |
382 | */ |
383 | void QVersitContactExporterPrivate::encodeUrl( |
384 | const QContactDetail& detail, |
385 | QList<QVersitProperty>* generatedProperties, |
386 | QSet<int>* processedFields) |
387 | { |
388 | const QContactUrl &contactUrl = static_cast<const QContactUrl &>(detail); |
389 | QList<int> contactUrlContextInt; |
390 | |
391 | for (int i=0; i<contactUrl.contexts().count(); i++) |
392 | contactUrlContextInt << contactUrl.contexts().at(i); |
393 | |
394 | QVersitProperty property; |
395 | property.setName(mPropertyMappings.value(akey: detail.type()).second); |
396 | encodeParameters(property, detailType: detail.type(), contexts: contactUrlContextInt); |
397 | // The vCard specifications do not define any TYPEs for URL property. |
398 | // No need to try to convert the subtypes to TYPEs. |
399 | property.setValue(contactUrl.url()); |
400 | *generatedProperties << property; |
401 | *processedFields << QContactUrl::FieldContext |
402 | << QContactUrl::FieldUrl; |
403 | } |
404 | |
405 | /*! |
406 | * Encode Uid Field Information into the Versit Document |
407 | */ |
408 | void QVersitContactExporterPrivate::encodeUid( |
409 | const QContactDetail& detail, |
410 | QList<QVersitProperty>* generatedProperties, |
411 | QSet<int>* processedFields) |
412 | { |
413 | const QContactGuid &uid = static_cast<const QContactGuid &>(detail); |
414 | QVersitProperty property; |
415 | property.setName(mPropertyMappings.value(akey: detail.type()).second); |
416 | property.setValue(uid.guid()); |
417 | *generatedProperties << property; |
418 | *processedFields << QContactGuid::FieldGuid; |
419 | } |
420 | |
421 | /*! |
422 | * Encode REV Field Information into the Versit Document |
423 | */ |
424 | void QVersitContactExporterPrivate::encodeRev( |
425 | const QContactDetail& detail, |
426 | QList<QVersitProperty>* generatedProperties, |
427 | QSet<int>* processedFields) |
428 | { |
429 | const QContactTimestamp &rev = static_cast<const QContactTimestamp &>(detail); |
430 | QString value; |
431 | QVersitProperty property; |
432 | property.setName(mPropertyMappings.value(akey: detail.type()).second); |
433 | if ( rev.lastModified().toString(format: Qt::ISODate).size() ) { |
434 | if ( rev.lastModified().timeSpec() == Qt::UTC ) { |
435 | value = rev.lastModified().toString(format: Qt::ISODate); |
436 | if( !value.endsWith(c: QLatin1Char('Z'), cs: Qt::CaseInsensitive) ) { |
437 | value += QLatin1Char('Z'); |
438 | } |
439 | } |
440 | else { |
441 | value = rev.lastModified().toString(format: Qt::ISODate); |
442 | } |
443 | property.setValue(value); |
444 | *generatedProperties << property; |
445 | *processedFields << QContactTimestamp::FieldModificationTimestamp; |
446 | } else if ( rev.created().toString(format: Qt::ISODate).size()) { |
447 | if ( rev.created().timeSpec() == Qt::UTC ) { |
448 | value = rev.created().toString(format: Qt::ISODate); |
449 | if( !value.endsWith(c: QLatin1Char('Z'), cs: Qt::CaseInsensitive) ) { |
450 | value += QLatin1Char('Z'); |
451 | } |
452 | } |
453 | else { |
454 | value = rev.created().toString(format: Qt::ISODate); |
455 | } |
456 | property.setValue(value); |
457 | *generatedProperties << property; |
458 | *processedFields << QContactTimestamp::FieldCreationTimestamp; |
459 | } |
460 | } |
461 | |
462 | /*! |
463 | * Encode Contact Version Field Information into the Versit Document |
464 | */ |
465 | void QVersitContactExporterPrivate::encodeVersion( |
466 | const QContactDetail& detail, |
467 | QList<QVersitProperty>* generatedProperties, |
468 | QSet<int>* processedFields) |
469 | { |
470 | const QContactVersion &version = static_cast<const QContactVersion &>(detail); |
471 | QVersitProperty property; |
472 | property.setName(mPropertyMappings.value(akey: detail.type()).second); |
473 | QStringList values(QString::number(version.sequenceNumber())); |
474 | values.append(t: QString::fromLocal8Bit(str: version.extendedVersion())); |
475 | property.setValue(values); |
476 | property.setValueType(QVersitProperty::CompoundType); |
477 | *generatedProperties << property; |
478 | *processedFields << QContactVersion::FieldSequenceNumber |
479 | << QContactVersion::FieldExtendedVersion; |
480 | } |
481 | |
482 | /*! |
483 | * Encode BirthDay Field Information into the Versit Document |
484 | */ |
485 | void QVersitContactExporterPrivate::encodeBirthDay( |
486 | const QContactDetail& detail, |
487 | QList<QVersitProperty>* generatedProperties, |
488 | QSet<int>* processedFields) |
489 | { |
490 | const QContactBirthday &bday = static_cast<const QContactBirthday &>(detail); |
491 | QVersitProperty property; |
492 | property.setName(mPropertyMappings.value(akey: detail.type()).second); |
493 | QVariant variant = bday.value(field: QContactBirthday::FieldBirthday); |
494 | QString value; |
495 | if (variant.type() == QVariant::Date) { |
496 | value = variant.toDate().toString(format: Qt::ISODate); |
497 | } else if (variant.type() == QVariant::DateTime) { |
498 | value = variant.toDateTime().toString(format: Qt::ISODate); |
499 | } else { |
500 | return; |
501 | } |
502 | property.setValue(value); |
503 | *generatedProperties << property; |
504 | *processedFields << QContactBirthday::FieldBirthday; |
505 | } |
506 | |
507 | /*! |
508 | * Encodes displaylabel property information into the Versit Document |
509 | */ |
510 | void QVersitContactExporterPrivate::encodeDisplayLabel( |
511 | const QContactDetail &detail, |
512 | const QVersitDocument& document, |
513 | QList<QVersitProperty>* removedProperties, |
514 | QList<QVersitProperty>* generatedProperties, |
515 | QSet<int>* processedFields) |
516 | { |
517 | const QContactDisplayLabel &displaylabelDetail = static_cast<const QContactDisplayLabel &>(detail); |
518 | QVersitProperty property = |
519 | VersitUtils::takeProperty(document, QStringLiteral("FN" ), toBeRemoved: removedProperties); |
520 | property.setName(QStringLiteral("FN" )); |
521 | property.setValue(displaylabelDetail.label()); |
522 | *generatedProperties << property; |
523 | *processedFields << QContactDisplayLabel::FieldLabel; |
524 | } |
525 | |
526 | /*! |
527 | * Encode Comment i.e. Note Field Information into the Versit Document |
528 | */ |
529 | void QVersitContactExporterPrivate::encodeNote( |
530 | const QContactDetail& detail, |
531 | QList<QVersitProperty>* generatedProperties, |
532 | QSet<int>* processedFields) |
533 | { |
534 | const QContactNote &contactNote = static_cast<const QContactNote &>(detail); |
535 | QVersitProperty property; |
536 | property.setName(mPropertyMappings.value(akey: detail.type()).second); |
537 | property.setValue(contactNote.note()); |
538 | *generatedProperties << property; |
539 | *processedFields << QContactNote::FieldNote; |
540 | } |
541 | |
542 | /*! |
543 | * Encode Geo Prpoperties Field Information into the Versit Document |
544 | */ |
545 | void QVersitContactExporterPrivate::encodeGeoLocation( |
546 | const QContactDetail& detail, |
547 | QList<QVersitProperty>* generatedProperties, |
548 | QSet<int>* processedFields) |
549 | { |
550 | const QContactGeoLocation &geoLocation = static_cast<const QContactGeoLocation &>(detail); |
551 | QVersitProperty property; |
552 | property.setName(mPropertyMappings.value(akey: detail.type()).second); |
553 | property.setValue(QStringList() << QString::number(geoLocation.latitude()) |
554 | << QString::number(geoLocation.longitude())); |
555 | property.setValueType(QVersitProperty::CompoundType); |
556 | *generatedProperties << property; |
557 | *processedFields << QContactGeoLocation::FieldLongitude |
558 | << QContactGeoLocation::FieldLatitude; |
559 | } |
560 | |
561 | /*! |
562 | * Encode organization properties to the versit document |
563 | */ |
564 | void QVersitContactExporterPrivate::encodeOrganization( |
565 | const QContactDetail& detail, |
566 | QList<QVersitProperty>* generatedProperties, |
567 | QSet<int>* processedFields) |
568 | { |
569 | const QContactOrganization &organization = static_cast<const QContactOrganization &>(detail); |
570 | if (organization.title().length() > 0) { |
571 | QVersitProperty property; |
572 | property.setName(QStringLiteral("TITLE" )); |
573 | property.setValue(organization.title()); |
574 | *generatedProperties << property; |
575 | *processedFields << QContactOrganization::FieldTitle; |
576 | } |
577 | if (organization.name().length() > 0 || organization.department().size() > 0) { |
578 | QVersitProperty property; |
579 | property.setName(QStringLiteral("ORG" )); |
580 | QStringList values(organization.name()); |
581 | values.append(t: organization.department()); |
582 | property.setValue(values); |
583 | property.setValueType(QVersitProperty::CompoundType); |
584 | *generatedProperties << property; |
585 | *processedFields << QContactOrganization::FieldName |
586 | << QContactOrganization::FieldDepartment; |
587 | } |
588 | if (organization.logoUrl().isValid()) { |
589 | QVersitProperty property; |
590 | if (encodeContentFromFile(resourcePath: organization.logoUrl().toString(), property)) { |
591 | property.setName(QStringLiteral("LOGO" )); |
592 | *generatedProperties << property; |
593 | *processedFields << QContactOrganization::FieldLogoUrl; |
594 | } |
595 | } |
596 | if (organization.assistantName().length() > 0) { |
597 | QVersitProperty property; |
598 | property.setName(QStringLiteral("X-ASSISTANT" )); |
599 | property.setValue(organization.assistantName()); |
600 | *generatedProperties << property; |
601 | *processedFields << QContactOrganization::FieldAssistantName; |
602 | } |
603 | |
604 | if (organization.role().length() > 0) { |
605 | QVersitProperty property; |
606 | property.setName(QStringLiteral("ROLE" )); |
607 | property.setValue(organization.role()); |
608 | *generatedProperties << property; |
609 | *processedFields << QContactOrganization::FieldRole; |
610 | } |
611 | } |
612 | |
613 | void QVersitContactExporterPrivate::encodeRingtone( |
614 | const QContactDetail &detail, |
615 | QList<QVersitProperty>* generatedProperties, |
616 | QSet<int>* processedFields) |
617 | { |
618 | const QContactRingtone &ringtone = static_cast<const QContactRingtone &>(detail); |
619 | QVersitProperty property; |
620 | property.setName(mPropertyMappings.value(akey: detail.type()).second); |
621 | QUrl audioUrl(ringtone.audioRingtoneUrl()); |
622 | // Url value |
623 | if (!audioUrl.scheme().isEmpty() && !audioUrl.host().isEmpty() && |
624 | audioUrl.scheme() != QStringLiteral("file" )) { |
625 | property.insertParameter(QStringLiteral("VALUE" ), QStringLiteral("URL" )); |
626 | property.setValue(audioUrl.toString()); |
627 | *generatedProperties << property; |
628 | *processedFields << QContactRingtone::FieldAudioRingtoneUrl; |
629 | // Local value |
630 | } else if (encodeContentFromFile(resourcePath: ringtone.audioRingtoneUrl().toLocalFile(), property)) { |
631 | *generatedProperties << property; |
632 | *processedFields << QContactRingtone::FieldAudioRingtoneUrl; |
633 | } |
634 | } |
635 | |
636 | /*! |
637 | * Encode avatar URIs into the Versit Document |
638 | */ |
639 | void QVersitContactExporterPrivate::encodeAvatar( |
640 | const QContactDetail &detail, |
641 | QList<QVersitProperty>* generatedProperties, |
642 | QSet<int>* processedFields) |
643 | { |
644 | QVersitProperty property; |
645 | property.setName(QStringLiteral("PHOTO" )); |
646 | const QContactAvatar &contactAvatar = static_cast<const QContactAvatar &>(detail); |
647 | QUrl imageUrl(contactAvatar.imageUrl()); |
648 | // XXX: fix up this mess: checking the scheme here and in encodeContentFromFile, |
649 | // organisation logo and ringtone are QStrings but avatar is a QUrl |
650 | if (!imageUrl.scheme().isEmpty() |
651 | && !imageUrl.host().isEmpty() |
652 | && imageUrl.scheme() != QStringLiteral("file" )) { |
653 | property.insertParameter(QStringLiteral("VALUE" ), QStringLiteral("URL" )); |
654 | property.setValue(imageUrl.toString()); |
655 | *generatedProperties << property; |
656 | *processedFields << QContactAvatar::FieldImageUrl; |
657 | } else { |
658 | if (encodeContentFromFile(resourcePath: contactAvatar.imageUrl().toLocalFile(), property)) { |
659 | *generatedProperties << property; |
660 | *processedFields << QContactAvatar::FieldImageUrl; |
661 | } |
662 | } |
663 | } |
664 | |
665 | /*! |
666 | * Encode gender property information into Versit Document |
667 | */ |
668 | void QVersitContactExporterPrivate::encodeGender( |
669 | const QContactDetail &detail, |
670 | QList<QVersitProperty>* generatedProperties, |
671 | QSet<int>* processedFields) |
672 | { |
673 | const QContactGender &gender = static_cast<const QContactGender &>(detail); |
674 | QVersitProperty property; |
675 | property.setName(mPropertyMappings.value(akey: detail.type()).second); |
676 | switch (gender.gender()) { |
677 | case QContactGender::GenderMale: |
678 | property.setValue(QStringLiteral("Male" )); |
679 | break; |
680 | case QContactGender::GenderFemale: |
681 | property.setValue(QStringLiteral("Female" )); |
682 | break; |
683 | case QContactGender::GenderUnspecified: |
684 | property.setValue(QStringLiteral("Unspecified" )); |
685 | break; |
686 | default: |
687 | // May only happen if new gender values are added to QContactGender |
688 | // without adding them support above. |
689 | qWarning() << "Trying to encode unknown gender value." ; |
690 | return; |
691 | } |
692 | *generatedProperties << property; |
693 | *processedFields << QContactGender::FieldGender; |
694 | } |
695 | |
696 | /*! |
697 | * Encodes nickname property information into the Versit Document |
698 | */ |
699 | void QVersitContactExporterPrivate::encodeNickname( |
700 | const QContactDetail &detail, |
701 | const QVersitDocument& document, |
702 | QList<QVersitProperty>* removedProperties, |
703 | QList<QVersitProperty>* generatedProperties, |
704 | QSet<int>* processedFields) |
705 | { |
706 | const QContactNickname &nicknameDetail = static_cast<const QContactNickname &>(detail); |
707 | QVersitProperty property = |
708 | VersitUtils::takeProperty(document, QStringLiteral("X-NICKNAME" ), toBeRemoved: removedProperties); |
709 | property.setName(QStringLiteral("X-NICKNAME" )); |
710 | QStringList value(property.variantValue().toStringList()); |
711 | value.append(t: nicknameDetail.nickname()); |
712 | property.setValue(value); |
713 | property.setValueType(QVersitProperty::ListType); |
714 | *generatedProperties << property; |
715 | *processedFields << QContactNickname::FieldNickname; |
716 | } |
717 | |
718 | /*! |
719 | * Encodes a contact tag into the Versit Document |
720 | */ |
721 | void QVersitContactExporterPrivate::encodeTag( |
722 | const QContactDetail &detail, |
723 | const QVersitDocument& document, |
724 | QList<QVersitProperty>* removedProperties, |
725 | QList<QVersitProperty>* generatedProperties, |
726 | QSet<int>* processedFields) |
727 | { |
728 | const QContactTag &tagDetail = static_cast<const QContactTag &>(detail); |
729 | QVersitProperty property = |
730 | VersitUtils::takeProperty(document, QStringLiteral("CATEGORIES" ), toBeRemoved: removedProperties); |
731 | property.setName(QStringLiteral("CATEGORIES" )); |
732 | QStringList value(property.variantValue().toStringList()); |
733 | value.append(t: tagDetail.tag()); |
734 | property.setValue(value); |
735 | property.setValueType(QVersitProperty::ListType); |
736 | *generatedProperties << property; |
737 | *processedFields << QContactTag::FieldTag; |
738 | } |
739 | |
740 | /*! |
741 | * Encode anniversary information into Versit Document |
742 | */ |
743 | void QVersitContactExporterPrivate::encodeAnniversary( |
744 | const QContactDetail &detail, |
745 | QList<QVersitProperty>* generatedProperties, |
746 | QSet<int>* processedFields) |
747 | { |
748 | const QContactAnniversary &anniversary = static_cast<const QContactAnniversary &>(detail); |
749 | QVersitProperty property; |
750 | property.setName(mPropertyMappings.value(akey: detail.type()).second); |
751 | property.setValue(anniversary.originalDate().toString(format: Qt::ISODate)); |
752 | *generatedProperties << property; |
753 | *processedFields << QContactAnniversary::FieldOriginalDate; |
754 | } |
755 | |
756 | /*! |
757 | * Encode online account information into the Versit Document |
758 | */ |
759 | void QVersitContactExporterPrivate::encodeOnlineAccount( |
760 | const QContactDetail &detail, |
761 | QList<QVersitProperty>* generatedProperties, |
762 | QSet<int>* processedFields) |
763 | { |
764 | const QContactOnlineAccount &onlineAccount = static_cast<const QContactOnlineAccount &>(detail); |
765 | QList<int> subTypes = onlineAccount.subTypes(); |
766 | |
767 | QContactOnlineAccount::Protocol protocol = onlineAccount.protocol(); |
768 | |
769 | QString propertyName; |
770 | |
771 | if (protocol == QContactOnlineAccount::ProtocolJabber) { |
772 | propertyName = QStringLiteral("X-JABBER" ); |
773 | } else if (protocol == QContactOnlineAccount::ProtocolAim) { |
774 | propertyName = QStringLiteral("X-AIM" ); |
775 | } else if (protocol == QContactOnlineAccount::ProtocolIcq) { |
776 | propertyName = QStringLiteral("X-ICQ" ); |
777 | } else if (protocol == QContactOnlineAccount::ProtocolMsn) { |
778 | propertyName = QStringLiteral("X-MSN" ); |
779 | } else if (protocol == QContactOnlineAccount::ProtocolQq) { |
780 | propertyName = QStringLiteral("X-QQ" ); |
781 | } else if (protocol == QContactOnlineAccount::ProtocolYahoo) { |
782 | propertyName = QStringLiteral("X-YAHOO" ); |
783 | } else if (protocol == QContactOnlineAccount::ProtocolSkype) { |
784 | propertyName = QStringLiteral("X-SKYPE" ); |
785 | } else if (subTypes.contains(t: QContactOnlineAccount::SubTypeSip) || |
786 | subTypes.contains(t: QContactOnlineAccount::SubTypeSipVoip) || |
787 | subTypes.contains(t: QContactOnlineAccount::SubTypeVideoShare)) { |
788 | propertyName = QStringLiteral("X-SIP" ); |
789 | } else if (subTypes.contains(t: QContactOnlineAccount::SubTypeImpp)) { |
790 | propertyName = QStringLiteral("X-IMPP" ); |
791 | } |
792 | |
793 | if (!propertyName.isEmpty()) { |
794 | QList<int> onlineAccountContextInt; |
795 | |
796 | for (int i=0; i<onlineAccount.contexts().count(); i++) |
797 | onlineAccountContextInt << onlineAccount.contexts().at(i); |
798 | |
799 | QVersitProperty property; |
800 | encodeParameters(property, detailType: detail.type(), contexts: onlineAccountContextInt, subTypes); |
801 | property.setName(propertyName); |
802 | property.setValue(onlineAccount.accountUri()); |
803 | *generatedProperties << property; |
804 | *processedFields << QContactOnlineAccount::FieldSubTypes |
805 | << QContactOnlineAccount::FieldAccountUri; |
806 | } |
807 | } |
808 | |
809 | /*! |
810 | * Encode family versit property if its supported in Versit Document |
811 | */ |
812 | void QVersitContactExporterPrivate::encodeFamily( |
813 | const QContactDetail &detail, |
814 | QList<QVersitProperty>* generatedProperties, |
815 | QSet<int>* processedFields) |
816 | { |
817 | const QContactFamily &family = static_cast<const QContactFamily &>(detail); |
818 | |
819 | if (family.spouse().size()) { |
820 | QVersitProperty property; |
821 | property.setName(QStringLiteral("X-SPOUSE" )); |
822 | property.setValue(family.spouse()); |
823 | *generatedProperties << property; |
824 | *processedFields << QContactFamily::FieldSpouse; |
825 | } |
826 | |
827 | if (family.children().size()) { |
828 | QVersitProperty property; |
829 | property.setName(QStringLiteral("X-CHILDREN" )); |
830 | property.setValue(family.children()); |
831 | property.setValueType(QVersitProperty::ListType); |
832 | *generatedProperties << property; |
833 | *processedFields << QContactFamily::FieldChildren; |
834 | } |
835 | } |
836 | |
837 | /*! |
838 | * Encode favorite versit property if its supported in Versit Document |
839 | */ |
840 | void QVersitContactExporterPrivate::encodeFavorite( |
841 | const QContactDetail &detail, |
842 | QList<QVersitProperty>* generatedProperties, |
843 | QSet<int>* processedFields) |
844 | { |
845 | const QContactFavorite &favorite = static_cast<const QContactFavorite &>(detail); |
846 | QVersitProperty property; |
847 | property.setName(mPropertyMappings.value(akey: detail.type()).second); |
848 | QString isFavorite = favorite.isFavorite() ? QStringLiteral("true" ) : QStringLiteral("false" ); |
849 | property.setValue(QStringList() << isFavorite |
850 | << QString::number(favorite.index())); |
851 | property.setValueType(QVersitProperty::CompoundType); |
852 | *generatedProperties << property; |
853 | *processedFields << QContactFavorite::FieldFavorite |
854 | << QContactFavorite::FieldIndex; |
855 | } |
856 | |
857 | /*! |
858 | * Encode extended detail into the Versit Document |
859 | */ |
860 | void QVersitContactExporterPrivate::encodeExtendedDetail( |
861 | const QContactDetail &detail, |
862 | QList<QVersitProperty>* generatedProperties, |
863 | QSet<int>* processedFields) |
864 | { |
865 | const QContactExtendedDetail &extendedDetail = |
866 | static_cast<const QContactExtendedDetail &>(detail); |
867 | QVersitProperty property; |
868 | property.setName(mPropertyMappings.value(akey: extendedDetail.type()).second); |
869 | QString dataAsJson; |
870 | if (VersitUtils::convertToJson(data: extendedDetail.data(), json: &dataAsJson)) { |
871 | property.setValue(QStringList() << extendedDetail.name() << dataAsJson); |
872 | } else { |
873 | qWarning() << Q_FUNC_INFO << "Failed to export an extended detail" ; |
874 | return; |
875 | } |
876 | property.setValueType(QVersitProperty::CompoundType); |
877 | *generatedProperties << property; |
878 | *processedFields << QContactExtendedDetail::FieldName |
879 | << QContactExtendedDetail::FieldData; |
880 | } |
881 | |
882 | /*! |
883 | * Check if \a resourceIdentifier represents a valid remote resource |
884 | */ |
885 | bool QVersitContactExporterPrivate::isValidRemoteUrl( |
886 | const QString& resourceIdentifier) |
887 | { |
888 | QUrl remoteResource(resourceIdentifier); |
889 | if ((!remoteResource.scheme().isEmpty() && |
890 | !remoteResource.host().isEmpty()) || |
891 | resourceIdentifier.contains(QStringLiteral("www." ), cs: Qt::CaseInsensitive)) |
892 | return true; |
893 | return false; |
894 | } |
895 | |
896 | /*! |
897 | * Encode parameters to \a property |
898 | */ |
899 | void QVersitContactExporterPrivate::encodeParameters( |
900 | QVersitProperty& property, |
901 | const QContactDetail::DetailType detailType, |
902 | const QList<int>& contexts, |
903 | const QList<int>& subTypes) |
904 | { |
905 | QList<int> contextsList(contexts); // Contexts should be encoded first |
906 | QList<int> subtypesList(subTypes); // Contexts should be encoded first |
907 | |
908 | while (!contextsList.isEmpty()) { |
909 | int value = contextsList.takeLast(); |
910 | QString mappedValue = mContextMappings.value(akey: value); |
911 | if (mappedValue.length() > 0) { |
912 | // QVersitProperty::addParameter inserts into beginning. |
913 | // This is why the last value is taken from the list |
914 | property.insertParameter(QStringLiteral("TYPE" ),value: mappedValue); |
915 | } |
916 | } |
917 | |
918 | QSet<int> inserted; |
919 | while (!subtypesList.isEmpty()) { |
920 | int value = subtypesList.takeLast(); |
921 | if (inserted.contains(value)) |
922 | continue; |
923 | |
924 | inserted.insert(value); |
925 | QString mappedValue = mSubTypeMappings.value(akey: QPair<QContactDetail::DetailType, int>( |
926 | detailType, value)); |
927 | if (mappedValue.length() > 0) { |
928 | // QVersitProperty::addParameter inserts into beginning. |
929 | // This is why the last value is taken from the list |
930 | property.insertParameter(QStringLiteral("TYPE" ),value: mappedValue); |
931 | } |
932 | } |
933 | } |
934 | |
935 | /*! |
936 | * Encode embedded content from the given \a resourcePath into \a property. |
937 | */ |
938 | bool QVersitContactExporterPrivate::encodeContentFromFile(const QString& resourcePath, |
939 | QVersitProperty& property) |
940 | { |
941 | bool encodeContent = false; |
942 | QVariant value; |
943 | QByteArray imageData; |
944 | QString mimeType; |
945 | if (isValidRemoteUrl( resourceIdentifier: resourcePath )) { |
946 | encodeContent = true; |
947 | value.setValue(resourcePath); |
948 | property.insertParameter(QStringLiteral("VALUE" ), QStringLiteral("uri" )); |
949 | } else if (mResourceHandler |
950 | && mResourceHandler->loadResource(location: resourcePath, contents: &imageData, mimeType: &mimeType)) { |
951 | value.setValue(imageData); |
952 | if (!mimeType.isEmpty()) { |
953 | // If mimeType is (eg.) "image/jpeg", set type parameter to "JPEG" |
954 | int slashIndex = mimeType.indexOf(c: QLatin1Char('/')); |
955 | if (slashIndex >= 0) |
956 | property.insertParameter(QStringLiteral("TYPE" ), |
957 | value: mimeType.remove(i: 0, len: slashIndex+1).toUpper()); |
958 | } |
959 | encodeContent = true; |
960 | } else { |
961 | // The file doesn't exist. Don't encode the path to a local file. |
962 | } |
963 | property.setValue(value); |
964 | return encodeContent; |
965 | } |
966 | |
967 | QT_END_NAMESPACE_VERSIT |
968 | |