Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge branch 'jb50555' into 'master'
[contacts] Allow import/export to various collections. Contributes to JB#50555

See merge request mer-core/nemo-qml-plugin-contacts!43
  • Loading branch information
chriadam committed Oct 6, 2020
2 parents fdef280 + a764b30 commit 98b0a8c
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 76 deletions.
135 changes: 72 additions & 63 deletions lib/seasideimport.cpp
Expand Up @@ -49,12 +49,19 @@ namespace {
}
}

QList<QContact> SeasideImport::buildImportContacts(const QList<QVersitDocument> &details, int *newCount, int *updatedCount, int *ignoredCount, SeasideContactBuilder *contactBuilder)
QList<QContact> SeasideImport::buildImportContacts(
const QList<QVersitDocument> &details,
int *newCount,
int *updatedCount,
int *ignoredCount,
SeasideContactBuilder *contactBuilder,
bool skipLocalDupDetection)
{
if (newCount)
*newCount = 0;
if (updatedCount)
*updatedCount = 0;
int existingCount = 0;
bool eraseMatch = false;

SeasideContactBuilder *builder = contactBuilder
Expand All @@ -81,77 +88,79 @@ QList<QContact> SeasideImport::buildImportContacts(const QList<QVersitDocument>
}
}

// Build up information about local device contacts, so we can detect matches
// in order to correctly set the appropriate ContactId in the imported contacts
// prior to save (thereby ensuring correct add vs update save semantics).
builder->buildLocalDeviceContactIndexes();

// Find any imported contacts that match contacts we already have
QMap<QContactId, int> existingIds;
it = importedContacts.begin();
while (it != importedContacts.end()) {
QContactId existingId = builder->matchingLocalContactId(*it);
if (!existingId.isNull()) {
QMap<QContactId, int>::iterator eit = existingIds.find(existingId);
if (eit == existingIds.end()) {
// this match hasn't been seen before.
existingIds.insert(existingId, (it - importedContacts.begin()));
++it;
} else {
// another import contact which matches that local contact has
// been seen already. Merge these both-matching import contacts.
QContact &previous(importedContacts[*eit]);
builder->mergeImportIntoImport(previous, *it, &eraseMatch);
if (eraseMatch) {
it = importedContacts.erase(it);
} else {
if (!skipLocalDupDetection) {
// Build up information about local device contacts, so we can detect matches
// in order to correctly set the appropriate ContactId in the imported contacts
// prior to save (thereby ensuring correct add vs update save semantics).
builder->buildLocalDeviceContactIndexes();

// Find any imported contacts that match contacts we already have
QMap<QContactId, int> existingIds;
it = importedContacts.begin();
while (it != importedContacts.end()) {
QContactId existingId = builder->matchingLocalContactId(*it);
if (!existingId.isNull()) {
QMap<QContactId, int>::iterator eit = existingIds.find(existingId);
if (eit == existingIds.end()) {
// this match hasn't been seen before.
existingIds.insert(existingId, (it - importedContacts.begin()));
++it;
} else {
// another import contact which matches that local contact has
// been seen already. Merge these both-matching import contacts.
QContact &previous(importedContacts[*eit]);
builder->mergeImportIntoImport(previous, *it, &eraseMatch);
if (eraseMatch) {
it = importedContacts.erase(it);
} else {
++it;
}
}
} else {
++it;
}
} else {
++it;
}
}

int existingCount(existingIds.count());
if (existingCount > 0) {
// Retrieve all the contacts that we have matches for
QContactIdFilter idFilter;
idFilter.setIds(existingIds.keys());

QSet<QContactId> modifiedContacts;
QSet<QContactId> unmodifiedContacts;
QHash<QContactId, bool> unmodifiedErase;

foreach (const QContact &contact, builder->manager()->contacts(idFilter & builder->mergeSubsetFilter(), QList<QContactSortOrder>(), basicFetchHint())) {
QMap<QContactId, int>::const_iterator it = existingIds.find(contact.id());
if (it != existingIds.end()) {
// Update the existing version of the contact with any new details
QContact &importContact(importedContacts[*it]);
bool modified = builder->mergeLocalIntoImport(importContact, contact, &eraseMatch);
if (modified) {
modifiedContacts.insert(importContact.id());
existingCount = existingIds.count();
if (existingCount > 0) {
// Retrieve all the contacts that we have matches for
QContactIdFilter idFilter;
idFilter.setIds(existingIds.keys());

QSet<QContactId> modifiedContacts;
QSet<QContactId> unmodifiedContacts;
QHash<QContactId, bool> unmodifiedErase;

foreach (const QContact &contact, builder->manager()->contacts(idFilter & builder->mergeSubsetFilter(), QList<QContactSortOrder>(), basicFetchHint())) {
QMap<QContactId, int>::const_iterator it = existingIds.find(contact.id());
if (it != existingIds.end()) {
// Update the existing version of the contact with any new details
QContact &importContact(importedContacts[*it]);
bool modified = builder->mergeLocalIntoImport(importContact, contact, &eraseMatch);
if (modified) {
modifiedContacts.insert(importContact.id());
} else {
unmodifiedContacts.insert(importContact.id());
unmodifiedErase.insert(importContact.id(), eraseMatch);
}
} else {
unmodifiedContacts.insert(importContact.id());
unmodifiedErase.insert(importContact.id(), eraseMatch);
qWarning() << "unable to update existing contact:" << contact.id();
}
} else {
qWarning() << "unable to update existing contact:" << contact.id();
}
}

if (!unmodifiedContacts.isEmpty()) {
QList<QContact>::iterator it = importedContacts.begin();
while (it != importedContacts.end()) {
const QContact &importContact(*it);
const QContactId contactId(importContact.id());

if (!modifiedContacts.contains(contactId) && unmodifiedContacts.contains(contactId) && unmodifiedErase.value(contactId, false) == true) {
// This contact was not modified by import and should be erased from the import list - don't update it
it = importedContacts.erase(it);
--existingCount;
} else {
++it;
if (!unmodifiedContacts.isEmpty()) {
QList<QContact>::iterator it = importedContacts.begin();
while (it != importedContacts.end()) {
const QContact &importContact(*it);
const QContactId contactId(importContact.id());

if (!modifiedContacts.contains(contactId) && unmodifiedContacts.contains(contactId) && unmodifiedErase.value(contactId, false) == true) {
// This contact was not modified by import and should be erased from the import list - don't update it
it = importedContacts.erase(it);
--existingCount;
} else {
++it;
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion lib/seasideimport.h
Expand Up @@ -48,7 +48,7 @@ class CONTACTCACHE_EXPORT SeasideImport
~SeasideImport();

public:
static QList<QContact> buildImportContacts(const QList<QVersitDocument> &details, int *newCount = 0, int *updatedCount = 0, int *ignoredCount = 0, SeasideContactBuilder *builder = 0);
static QList<QContact> buildImportContacts(const QList<QVersitDocument> &details, int *newCount = 0, int *updatedCount = 0, int *ignoredCount = 0, SeasideContactBuilder *builder = 0, bool skipLocalDupDetection = false);
};

#endif
42 changes: 30 additions & 12 deletions tools/vcardconverter/main.cpp
Expand Up @@ -67,15 +67,14 @@ void errorMessage(const QString &s)

void invalidUsage(const QString &app)
{
errorMessage(QString::fromLatin1("Usage: %1 [-e | --export] <filename>").arg(app));
errorMessage(QString::fromLatin1("Usage: %1 [-e | --export] <filename> [<collectionId>]").arg(app));
::exit(1);
}

QContactFilter localContactFilter(const QContactCollectionId &localAddressbookId)
QContactFilter collectionFilter(const QContactCollectionId &collectionId)
{
// Contacts that are local to the device belong to the "local" addressbook.
QContactCollectionFilter filter;
filter.setCollectionId(localAddressbookId);
filter.setCollectionId(collectionId);
return filter;
}

Expand All @@ -87,6 +86,7 @@ int main(int argc, char **argv)

bool import = true;
QString filename;
QString collection;

const QString app(QString::fromLatin1(argv[0]));

Expand All @@ -102,7 +102,11 @@ int main(int argc, char **argv)
invalidUsage(app);
}
} else {
filename = arg;
if (filename.isEmpty()) {
filename = arg;
} else {
collection = arg;
}
}
}

Expand All @@ -119,7 +123,21 @@ int main(int argc, char **argv)
}

QContactManager mgr(QString::fromLatin1("org.nemomobile.contacts.sqlite"));
const QContactCollectionId localAddressbookId(QtContactsSqliteExtensions::localCollectionId(mgr.managerUri()));
const QContactCollectionId collectionId(collection.isEmpty() ? QtContactsSqliteExtensions::localCollectionId(mgr.managerUri())
: QContactCollectionId::fromString(collection));
const bool collectionIsLocalAddressbook = collectionId == QtContactsSqliteExtensions::localCollectionId(mgr.managerUri());

if (!collectionIsLocalAddressbook) {
if (import) {
qDebug("Importing contact data to non-local addressbook - this data may be "
"synced to the remote account provider or application which owns "
"the collection!");
} else {
qDebug("Exporting non-local contacts - your usage of this data must comply "
"with the terms of service of the account provider or application "
"from which the data was synced!");
}
}

if (import) {
// Read the contacts from the VCF
Expand All @@ -128,12 +146,12 @@ int main(int argc, char **argv)
reader.waitForFinished();

// Get the import list which duplicates coalesced, and updates merged
int newCount;
int updatedCount;
QList<QContact> importedContacts(SeasideImport::buildImportContacts(reader.results(), &newCount, &updatedCount));
int newCount = reader.results().count();
int updatedCount = 0;
QList<QContact> importedContacts(SeasideImport::buildImportContacts(reader.results(), &newCount, &updatedCount, nullptr, nullptr, !collectionIsLocalAddressbook));
for (int i = 0; i < importedContacts.size(); ++i) {
QContact &c(importedContacts[i]);
c.setCollectionId(localAddressbookId);
c.setCollectionId(collectionId);
}

QString existingDesc(updatedCount ? QString::fromLatin1(" (updating %1 existing)").arg(updatedCount) : QString());
Expand Down Expand Up @@ -162,9 +180,9 @@ int main(int argc, char **argv)
}
qDebug("Wrote %d contacts", importedCount);
} else {
QList<QContact> localContacts(mgr.contacts(localContactFilter(localAddressbookId)));
QList<QContact> contacts(mgr.contacts(collectionFilter(collectionId)));

QList<QVersitDocument> documents(SeasideExport::buildExportContacts(localContacts));
QList<QVersitDocument> documents(SeasideExport::buildExportContacts(contacts));
qDebug("Exporting %d contacts", documents.count());

QVersitWriter writer(&vcf);
Expand Down

0 comments on commit 98b0a8c

Please sign in to comment.