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
48QTCONTACTS_USE_NAMESPACE
49
50QT_BEGIN_NAMESPACE_VERSIT
51
52/*!
53 * Constructor.
54 */
55QVersitContactExporterPrivate::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 */
96QVersitContactExporterPrivate::~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 */
108void 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 */
235void 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 */
258void 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 */
295void 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 */
323void 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 */
346void 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 */
383void 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 */
408void 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 */
424void 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 */
465void 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 */
485void 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 */
510void 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 */
529void 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 */
545void 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 */
564void 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
613void 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 */
639void 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 */
668void 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 */
699void 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 */
721void 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 */
743void 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 */
759void 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 */
812void 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 */
840void 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 */
860void 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 */
885bool 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 */
899void 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 */
938bool 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
967QT_END_NAMESPACE_VERSIT
968

source code of qtpim/src/versit/qversitcontactexporter_p.cpp