Merge lp:~renatofilho/buteo-sync-plugins-contacts/new-code into lp:buteo-sync-plugins-contacts
- new-code
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Michael Sheldon |
Approved revision: | 56 |
Merged at revision: | 4 |
Proposed branch: | lp:~renatofilho/buteo-sync-plugins-contacts/new-code |
Merge into: | lp:buteo-sync-plugins-contacts |
Diff against target: |
15372 lines (+14750/-0) 121 files modified
CMakeLists.txt (+59/-0) accounts/CMakeLists.txt (+14/-0) accounts/address-book-app.application (+11/-0) accounts/google-contacts.service (+6/-0) buteo-contact-client/CMakeLists.txt (+37/-0) buteo-contact-client/UAbstractRemoteSource.cpp (+178/-0) buteo-contact-client/UAbstractRemoteSource.h (+125/-0) buteo-contact-client/UAuth.cpp (+181/-0) buteo-contact-client/UAuth.h (+72/-0) buteo-contact-client/UContactsBackend.cpp (+572/-0) buteo-contact-client/UContactsBackend.h (+254/-0) buteo-contact-client/UContactsClient.cpp (+1028/-0) buteo-contact-client/UContactsClient.h (+170/-0) buteo-contact-client/UContactsCustomDetail.cpp (+56/-0) buteo-contact-client/UContactsCustomDetail.h (+52/-0) cmake/lcov.cmake (+72/-0) cmake_uninstall.cmake.in (+21/-0) config.h.in (+9/-0) google/CMakeLists.txt (+87/-0) google/GConfig.cpp (+52/-0) google/GConfig.h (+71/-0) google/GContactAtom.cpp (+209/-0) google/GContactAtom.h (+138/-0) google/GContactGroupsMap.cpp (+122/-0) google/GContactGroupsMap.h (+60/-0) google/GContactImageDownloader.cpp (+117/-0) google/GContactImageDownloader.h (+68/-0) google/GContactImageUploader.cpp (+204/-0) google/GContactImageUploader.h (+78/-0) google/GContactStream.cpp (+1446/-0) google/GContactStream.h (+190/-0) google/GContactsClient.cpp (+83/-0) google/GContactsClient.h (+58/-0) google/GRemoteSource.cpp (+720/-0) google/GRemoteSource.h (+95/-0) google/GTransport.cpp (+450/-0) google/GTransport.h (+103/-0) google/LICENSE (+24/-0) google/README.md (+4/-0) google/atom_global.h (+37/-0) google/buteo-gcontact-plugin_global.h (+37/-0) google/buteosyncfw_p.h (+40/-0) google/xmls/CMakeLists.txt (+2/-0) google/xmls/client/CMakeLists.txt (+3/-0) google/xmls/client/googlecontacts.xml (+8/-0) google/xmls/sync/CMakeLists.txt (+3/-0) google/xmls/sync/google-contacts.xml (+20/-0) storage-change-notifier/CMakeLists.txt (+1/-0) storage-change-notifier/contacts/CMakeLists.txt (+31/-0) storage-change-notifier/contacts/ContactsChangeNotifier.cpp (+95/-0) storage-change-notifier/contacts/ContactsChangeNotifier.h (+70/-0) storage-change-notifier/contacts/ContactsChangeNotifierPlugin.cpp (+101/-0) storage-change-notifier/contacts/ContactsChangeNotifierPlugin.h (+75/-0) tests/CMakeLists.txt (+4/-0) tests/plugins/CMakeLists.txt (+1/-0) tests/plugins/contacts/CMakeLists.txt (+12/-0) tests/plugins/contacts/memory.json (+3/-0) tests/plugins/contacts/qcontactmemorybackend.cpp (+1104/-0) tests/plugins/contacts/qcontactmemorybackend_p.h (+245/-0) tests/unittest/CMakeLists.txt (+93/-0) tests/unittest/GContactImageUploader.cpp (+108/-0) tests/unittest/GTransport.cpp (+249/-0) tests/unittest/MockAuthenticator.cpp (+49/-0) tests/unittest/MockAuthenticator.h (+49/-0) tests/unittest/MockRemoteSource.cpp (+273/-0) tests/unittest/MockRemoteSource.h (+74/-0) tests/unittest/TestContactsClient.cpp (+78/-0) tests/unittest/TestContactsClient.h (+59/-0) tests/unittest/TestContactsMain.cpp (+483/-0) tests/unittest/TestGRemoteSource.cpp (+571/-0) tests/unittest/TestGoogleContactParser.cpp (+1049/-0) tests/unittest/config-tests.h.in (+10/-0) tests/unittest/data/fast_sync_changed_both_sides_local.vcf (+47/-0) tests/unittest/data/fast_sync_changed_both_sides_local_result.vcf (+21/-0) tests/unittest/data/fast_sync_changed_both_sides_remote.vcf (+39/-0) tests/unittest/data/fast_sync_changed_both_sides_remote_result.vcf (+18/-0) tests/unittest/data/fast_sync_delete_same_contact_local.vcf (+47/-0) tests/unittest/data/fast_sync_delete_same_contact_local_result.vcf (+34/-0) tests/unittest/data/fast_sync_delete_same_contact_remote.vcf (+39/-0) tests/unittest/data/fast_sync_delete_same_contact_remote_result.vcf (+28/-0) tests/unittest/data/fast_sync_with_a_local_change_local.vcf (+46/-0) tests/unittest/data/fast_sync_with_a_local_change_local_result.vcf (+45/-0) tests/unittest/data/fast_sync_with_a_local_change_remote.vcf (+38/-0) tests/unittest/data/fast_sync_with_a_local_change_remote_result.vcf (+38/-0) tests/unittest/data/fast_sync_with_a_local_removal_local.vcf (+47/-0) tests/unittest/data/fast_sync_with_a_local_removal_local_result.vcf (+34/-0) tests/unittest/data/fast_sync_with_a_local_removal_remote.vcf (+38/-0) tests/unittest/data/fast_sync_with_a_local_removal_remote_result.vcf (+28/-0) tests/unittest/data/fast_sync_with_a_remote_change_local.vcf (+45/-0) tests/unittest/data/fast_sync_with_a_remote_change_local_result.vcf (+45/-0) tests/unittest/data/fast_sync_with_a_remote_change_remote.vcf (+38/-0) tests/unittest/data/fast_sync_with_a_remote_change_remote_result.vcf (+38/-0) tests/unittest/data/fast_sync_with_a_remote_removal_local.vcf (+46/-0) tests/unittest/data/fast_sync_with_a_remote_removal_local_result.vcf (+34/-0) tests/unittest/data/fast_sync_with_a_remote_removal_remote.vcf (+39/-0) tests/unittest/data/fast_sync_with_a_remote_removal_remote_result.vcf (+28/-0) tests/unittest/data/fast_sync_with_new_local_contact_local.vcf (+20/-0) tests/unittest/data/fast_sync_with_new_local_contact_local_result.vcf (+21/-0) tests/unittest/data/fast_sync_with_new_local_contact_remote.vcf (+8/-0) tests/unittest/data/fast_sync_with_new_local_contact_remote_result.vcf (+18/-0) tests/unittest/data/fast_sync_without_changes_local.vcf (+46/-0) tests/unittest/data/fast_sync_without_changes_local_result.vcf (+46/-0) tests/unittest/data/fast_sync_without_changes_remote.vcf (+38/-0) tests/unittest/data/fast_sync_without_changes_remote_result.vcf (+38/-0) tests/unittest/data/google_contact_created_page.txt (+25/-0) tests/unittest/data/google_contact_full_fetch.vcf (+200/-0) tests/unittest/data/google_contact_full_fetch_page_0.txt (+297/-0) tests/unittest/data/google_contact_full_fetch_page_1.txt (+158/-0) tests/unittest/data/google_contact_updated_reply.txt (+28/-0) tests/unittest/data/google_not_found_contact_response.txt (+31/-0) tests/unittest/data/google_single_entry.txt (+162/-0) tests/unittest/data/local_previous_synced_local.vcf (+10/-0) tests/unittest/data/local_previous_synced_local_result.vcf (+38/-0) tests/unittest/data/local_previous_synced_remote.vcf (+27/-0) tests/unittest/data/local_previous_synced_remote_result.vcf (+34/-0) tests/unittest/data/new_database_local.vcf (+7/-0) tests/unittest/data/new_database_local_result.vcf (+38/-0) tests/unittest/data/new_database_remote.vcf (+27/-0) tests/unittest/data/new_database_remote_result.vcf (+34/-0) tests/unittest/data/slow_sync_with_pages_remote.vcf (+143/-0) tests/unittest/profile-test.xml (+23/-0) |
To merge this branch: | bzr merge lp:~renatofilho/buteo-sync-plugins-contacts/new-code |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michael Sheldon (community) | Approve | ||
PS Jenkins bot | continuous-integration | Needs Fixing | |
Renato Araujo Oliveira Filho | Pending | ||
Review via email: mp+264335@code.launchpad.net |
Commit message
Implemented buteo contacts sync plugin for google, heavily based on:
- https:/
- https:/
Description of the change
- 6. By Renato Araujo Oliveira Filho
-
Added support for ringtones sync.
Fixed Notes parse.
Fixed Gender parse. - 7. By Renato Araujo Oliveira Filho
-
Trunk merged.
- 8. By Renato Araujo Oliveira Filho
-
Create contact source linked with the online account id.
- 9. By Renato Araujo Oliveira Filho
-
Use online account display name as local address book name.
- 10. By Renato Araujo Oliveira Filho
-
Update contact avatar with a empty string if its fail to download.
- 11. By Renato Araujo Oliveira Filho
-
Added coverage support on cmake. (use with make lcov)
Write more unit tests. - 12. By Renato Araujo Oliveira Filho
-
Increased test coverability.
- 13. By Renato Araujo Oliveira Filho
-
Check for "gd:etag" tag on photo link.
if a contact does not have a photo, then the photo link element has no gd:etag attribute.
PS Jenkins bot (ps-jenkins) wrote : | # |
- 14. By Renato Araujo Oliveira Filho
-
Fixed avatar parse.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:14
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 15. By Renato Araujo Oliveira Filho
-
Changed build to allow mock GContactImageUp
loader;
Created test for GContactImageUploader;
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:15
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 16. By Renato Araujo Oliveira Filho
-
Fix QContactOrganiz
ation parse. - 17. By Renato Araujo Oliveira Filho
-
Set 60s as interval for sync on change.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:17
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 18. By Renato Araujo Oliveira Filho
-
Added 'remote_
service_ name' key into sync profile template.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:18
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 19. By Renato Araujo Oliveira Filho
-
Changed page result size from 50 to 30.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:19
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Michael Sheldon (michael-sheldon) wrote : | # |
I see that you've now marked the buteo-sync-
Michael Sheldon (michael-sheldon) wrote : | # |
It is correct to say that these files are part of buteo-gcontact-
I've also added a number of small things in the diff comments (mostly just typos, but also a couple of questions about some TODOs and an unused variable).
(I've only reviewed the first 1000 lines of the diff so far, so there'll be more to come)
Michael Sheldon (michael-sheldon) wrote : | # |
Highlighted a number of places where you're using qDebug (most of the rest of the code uses LOG_DEBUG), I've only checked on the part of the diff shown on the MR (the first 5000 lines) so you may want to quickly grep the rest of the code as well.
- 20. By Renato Araujo Oliveira Filho
-
Replaced use of qDebug() to LOG_DEBUG
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:20
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Michael Sheldon (michael-sheldon) wrote : | # |
More in-line comments (only as far as line 2000 at the moment), mostly small typos but also a couple of questions in there too.
Renato Araujo Oliveira Filho (renatofilho) wrote : | # |
> I see that you've now marked the buteo-sync-
> conflicting with evolution-
> name the buteo plugin differently so that the two can co-exist? It might not
> be an issue on the phone, but I imagine a lot of desktop users might want to
> continue using evolution when in desktop mode.
The problem is that both install an online-account contact service file "google-
- 21. By Renato Araujo Oliveira Filho
-
Fixed typos.
Fixed project name on license header.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:21
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Renato Araujo Oliveira Filho (renatofilho) : | # |
- 22. By Renato Araujo Oliveira Filho
-
Fixed typos.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:22
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Michael Sheldon (michael-sheldon) wrote : | # |
Up to line 3000 now, still mostly just small corrections and a few questions.
- 23. By Renato Araujo Oliveira Filho
-
Purge contacts removed since last sync only.
Fixed sync time report. - 24. By Renato Araujo Oliveira Filho
-
Handle error in case of contact removed remote during a previous sync.
- 25. By Renato Araujo Oliveira Filho
-
Uncomment handleUploadError function call.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:25
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 26. By Renato Araujo Oliveira Filho
-
Delay 2 secs before fire sync finished signal. To wait until galera backed propagate the changes.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:26
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Michael Sheldon (michael-sheldon) wrote : | # |
Added diff comments up to line 5000, noticed a few seemingly unnecessary string conversions, otherwise just more tiny things and questions.
Michael Sheldon (michael-sheldon) wrote : | # |
Up to line 7000, Launchpad can't seem to handle more than 5000 lines, so I'll include further remarks in comments:
Line 5137
+ QContactExtende
+ group.setName(
+ group.setData("6");
Can you add a comment explaining the significance of "6"? (Judging from later code I'm guessing this is the 'My Contacts' group?)
Line 5472
+ protocolMap.
+ protocolMap.
+ protocolMap.
+ protocolMap.
+ protocolMap.
+ protocolMap.
+ protocolMap.
+ protocolMap.
I notice that this list doesn't include GOOGLE_TALK but an earlier one did, should they not match?
Line 6089
+ // keep downloader object live while GRemoreSource exists to avoid remove
+ // the temporary files used to store avatars
GRemoreSource -> GRemoteSource, remove -> removing
Line 6522
+ // FIXME
+ // m_unsupportedXm
+ // c.detail<
+ // remoteAddModCon
+ // m_contactEtags[
+ // c.setId(
+ // m_remoteAddMods
Can you add details about what needs fixing here? Also the same for line 6542
Line 6604
+ // TODO: If interested, check the value of error. But
+ // it is enough to say that it is a SYNC_CONNECTION
+ //emit syncFinished (Sync::
The code following this looks like it is now checking the type of sync error, so this TODO can probably be removed.
I also just noticed that all the updated copyright notices have a typo in the name, they should be "buteo-
- 27. By Renato Araujo Oliveira Filho
-
Handle contact not found '404', during a contact update.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:27
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Renato Araujo Oliveira Filho (renatofilho) : | # |
- 28. By Renato Araujo Oliveira Filho
-
Use "ClientId", "ClientSecret" values already present in account information.
- 29. By Renato Araujo Oliveira Filho
-
Replaced use of "QLatin1String" in favor or "QStringLiteral"
- 30. By Renato Araujo Oliveira Filho
-
Predefine string to keep consistence.
- 31. By Renato Araujo Oliveira Filho
-
Does not parse google online account as jabber account.
Renato Araujo Oliveira Filho (renatofilho) wrote : | # |
> Up to line 7000, Launchpad can't seem to handle more than 5000 lines, so I'll
> include further remarks in comments:
>
>
> Line 5137
> + QContactExtende
> + group.setName(
> + group.setData("6");
>
> Can you add a comment explaining the significance of "6"? (Judging from later
> code I'm guessing this is the 'My Contacts' group?)
Fixed rev. 32
>
>
> Line 5472
> + protocolMap.
> + protocolMap.
> + protocolMap.
> + protocolMap.
> + protocolMap.
> + protocolMap.
> + protocolMap.
> + protocolMap.
>
> I notice that this list doesn't include GOOGLE_TALK but an earlier one did,
> should they not match?
QContactOnlineA
QString protocolName = protocolMap.
Fixed in Rev. 31
>
>
> Line 6089
> + // keep downloader object live while GRemoreSource exists to avoid remove
> + // the temporary files used to store avatars
>
> GRemoreSource -> GRemoteSource, remove -> removing
Fixed rev 33.
>
>
> Line 6522
> + // FIXME
> + // m_unsupportedXm
> + // c.detail<
> + // remoteAddModCon
> + //
> m_contactEtags[
> c.detail<
> + // c.setId(
> ue(c.detail<
> + // m_remoteAddMods
>
> Can you add details about what needs fixing here? Also the same for line 6542
Fixed rev. 34
>
>
> Line 6604
> + // TODO: If interested, check the value of error. But
> + // it is enough to say that it is a SYNC_CONNECTION
> + //emit syncFinished (Sync::
>
> The code following this looks like it is now checking the type of sync error,
> so this TODO can probably be removed.
Fixed rev. 35.
>
>
> I also just noticed that all the updated copyright notices have a typo in the
> name, they should be "buteo-
> goole" (missing "g" in google).
Fixed rev. 36
- 32. By Renato Araujo Oliveira Filho
-
Added "FiXME" message for hardcoded google default group.
- 33. By Renato Araujo Oliveira Filho
-
Fixed typos in comments.
- 34. By Renato Araujo Oliveira Filho
-
Added more information about the FIXME comment.
- 35. By Renato Araujo Oliveira Filho
-
Removed old TODO comment.
- 36. By Renato Araujo Oliveira Filho
-
Fixed typo.
- 37. By Renato Araujo Oliveira Filho
-
Added missing test data file.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:31
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:37
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 38. By Renato Araujo Oliveira Filho
-
Propagate error from remote source to UContactsClient.
Handle error contact not found during a update operation, on UContactsClient class.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:38
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 39. By Renato Araujo Oliveira Filho
-
[google-contact] Make sure that the old contact avatar ulr get copied back to remote contact before return it back to UContactClient.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:39
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Michael Sheldon (michael-sheldon) wrote : | # |
This is the last batch of issues going all the way to the end of the diff, so once these have been addressed I'll do a quick check over the previous corrections and then we should be good to approve this from a general code review stand point.
Line 7321
+ // Include "X-HTTP-
+ //const void DELETE( const QString contactId );
Should this still be here (currently commented out)?
I think you need to add Canonical and yourself to google/LICENSE
Line 8099,
+ /*! \brief constructor
+ */
+ ~ContactsChange
constructor -> destructor
google/Makefile is currently in version control, presumably this can be removed as it's a generated file?
tests/unittest/
Line 11156
+ // remote source will be initialized after sucess
sucess -> success
Line 11187
+ QTest::newRow("page division exaclty") << 5
exaclty -> exactly
Line 11258
+ // trasaction commit will be fired once with empty list
trasaction -> transaction
Line 11751
+ // chek if avatar still the same
chek -> check
Line 11843
+ // check if the repported error is correct
repported -> reported
Line 11923
+ // check each contat details
contat -> contact
Line 12769
+ // Anniversay
Anniversay -> Anniversary
All the test VCF files except for tests/unittest/
Is there any package that supplies QContactMemoryB
Renato Araujo Oliveira Filho (renatofilho) wrote : | # |
> Line 7321
> + // Include "X-HTTP-
> avoid blocking of HTTP DELETE message by firewalls
> + //const void DELETE( const QString contactId );
>
> Should this still be here (currently commented out)?
Fixed rev. 40
>
> I think you need to add Canonical and yourself to google/LICENSE
Fixed rev. 41
>
>
> Line 8099,
> + /*! \brief constructor
> + */
> + ~ContactsChange
>
> constructor -> destructor
Fixed rev. 42
>
>
> google/Makefile is currently in version control, presumably this can be
> removed as it's a generated file?
Fixed rev. 43
>
>
> tests/unittest/
> run through dos2unix will fix this)
Fixed rev. 44
>
>
> Line 11156
> + // remote source will be initialized after sucess
> sucess -> success
>
>
> Line 11187
> + QTest::newRow("page division exaclty") << 5
> exaclty -> exactly
>
>
> Line 11258
> + // trasaction commit will be fired once with empty list
> trasaction -> transaction
>
>
> Line 11751
> + // chek if avatar still the same
> chek -> check
>
>
> Line 11843
> + // check if the repported error is correct
> repported -> reported
>
>
> Line 11923
> + // check each contat details
> contat -> contact
>
>
> Line 12769
> + // Anniversay
> Anniversay -> Anniversary
>
>
> All the test VCF files except for
> tests/unittest/
> endings.
The default EOL for vcards is the DOS format.
>
> Is there any package that supplies QContactMemoryB
> be linked against to remove the need to including the source for it as part of
> this package?
Yes QtPim package contains the memory backend. But I copied it to the porjects because I need some small changes necessary to help me to run my tests. And these changes does not make sens upstream they are very specific.
- 40. By Renato Araujo Oliveira Filho
-
Removed commented code.
- 41. By Renato Araujo Oliveira Filho
-
Updated LICENSE file.
- 42. By Renato Araujo Oliveira Filho
-
Fixed typo.
- 43. By Renato Araujo Oliveira Filho
-
Removed old file.
- 44. By Renato Araujo Oliveira Filho
-
Fixed file EOL syntax.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:44
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Michael Sheldon (michael-sheldon) wrote : | # |
Changes all look good, but I'm still rather concerned over the file name conflict as it means any existing apps that use evolution-
It seems an unnecessary restriction when the file could just be renamed to google-
Renato Araujo Oliveira Filho (renatofilho) wrote : | # |
> Changes all look good, but I'm still rather concerned over the file name
> conflict as it means any existing apps that use evolution-
> make use of online sync functionality if installed alongside address-book-app
> (e.g. pidgin, evolution, gnome-shell, etc.), which may put a number of desktop
> users off from trying address-book-app (or any other SDK apps depending on
> buteo).
>
> It seems an unnecessary restriction when the file could just be renamed to
> google-
It will conflict with "evolution-
It is not buteo which conflicts with this package it is buteo-google-
The address-book-app does not depend on "buteo-
Renato Araujo Oliveira Filho (renatofilho) wrote : | # |
> (e.g. pidgin, evolution, gnome-shell, etc.), which may put a number of desktop
> users off from trying address-book-app (or any other SDK apps depending on
> buteo).
My pidgin does not depend on "evolution-
Yeah if the gnome-shell depends on "evolution-
Michael Sheldon (michael-sheldon) wrote : | # |
Okay, as long as it's possible on a desktop system to have address-book-app and gnome-shell/
- 45. By Renato Araujo Oliveira Filho
-
Fixed typos.
- 46. By Renato Araujo Oliveira Filho
-
Only fetch contacts from "My Contacts" group.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:46
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 47. By Renato Araujo Oliveira Filho
-
Implemented sync progress notification.
- 48. By Renato Araujo Oliveira Filho
-
Updated unit test.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:47
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:48
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 49. By Renato Araujo Oliveira Filho
-
Updated unittest.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:49
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 50. By Renato Araujo Oliveira Filho
-
Optimize queries by remote-id using a cache.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:50
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 51. By Renato Araujo Oliveira Filho
-
Keep remoteId map in sync to avoid slow query.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:51
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 52. By Renato Araujo Oliveira Filho
-
Ask for invisible contacts during the sync.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:52
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 53. By Renato Araujo Oliveira Filho
-
Only uses 'invisible' filter if sync target is set.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:53
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 54. By Renato Araujo Oliveira Filho
-
Use "show-invisible" engine parameter to retrieve invisible contacts from server.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:54
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 55. By Renato Araujo Oliveira Filho
-
Initialize contacts backend after authentication.
Avoid to create the contacts source if the authentication failed.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:55
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 56. By Renato Araujo Oliveira Filho
-
Implemented abort function for GRemoteSource.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:56
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Michael Sheldon (michael-sheldon) wrote : | # |
I'm unclear on the use of stateChanged in UContactsClient now, it seems from the method definition that this has changed from providing one of a few predefined states to providing the progress percentage, however in the code there are places where it's still being called with the old states, e.g.
stateChanged(
vs.
stateChanged(
Renato Araujo Oliveira Filho (renatofilho) wrote : | # |
> I'm unclear on the use of stateChanged in UContactsClient now, it seems from
> the method definition that this has changed from providing one of a few
> predefined states to providing the progress percentage, however in the code
> there are places where it's still being called with the old states, e.g.
>
> stateChanged(
>
> vs.
>
> stateChanged(
In fact it can be used for both, the enums values like SYNC_PROGRESS_
Michael Sheldon (michael-sheldon) wrote : | # |
Ah, okay; in that case this all looks good to me.
Preview Diff
1 | === added file 'CMakeLists.txt' |
2 | --- CMakeLists.txt 1970-01-01 00:00:00 +0000 |
3 | +++ CMakeLists.txt 2015-09-24 17:49:24 +0000 |
4 | @@ -0,0 +1,59 @@ |
5 | +project(buteo-sync-plugins-google) |
6 | +cmake_minimum_required(VERSION 2.8.9) |
7 | + |
8 | +# Find includes in corresponding build directories |
9 | +set(CMAKE_INCLUDE_CURRENT_DIR ON) |
10 | + |
11 | +# Instruct CMake to run moc automatically when needed. |
12 | +set(CMAKE_AUTOMOC ON) |
13 | + |
14 | +# Standard install paths |
15 | +include(GNUInstallDirs) |
16 | + |
17 | +find_package(PkgConfig REQUIRED) |
18 | +find_package(Qt5Core REQUIRED) |
19 | +find_package(Qt5DBus REQUIRED) |
20 | +find_package(Qt5Contacts REQUIRED) |
21 | +find_package(Qt5Network REQUIRED) |
22 | +find_package(Qt5Versit REQUIRED) |
23 | + |
24 | +pkg_check_modules(BUTEOSYNCFW REQUIRED buteosyncfw5) |
25 | +pkg_check_modules(ACCOUNTS REQUIRED accounts-qt5>=1.10) |
26 | +pkg_check_modules(LIBSIGNON REQUIRED libsignon-qt5) |
27 | + |
28 | +set(BUTEOSYNCFW_PLUGIN_PATH "/usr/lib/buteo-plugins-qt5") |
29 | + |
30 | +# config file |
31 | +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/config.h.in" |
32 | + "${CMAKE_CURRENT_BINARY_DIR}/config.h" |
33 | + IMMEDIATE @ONLY) |
34 | + |
35 | + |
36 | +# Coverage tools |
37 | +OPTION(ENABLE_COVERAGE "Build with coverage analysis support" OFF) |
38 | +if(ENABLE_COVERAGE) |
39 | + message(STATUS "Using coverage flags") |
40 | + find_program(COVERAGE_COMMAND gcov) |
41 | + if(NOT COVERAGE_COMMAND) |
42 | + message(FATAL_ERROR "gcov command not found") |
43 | + endif() |
44 | + SET(CMAKE_C_FLAGS "-g -O0 -Wall -fprofile-arcs -ftest-coverage") |
45 | + SET(CMAKE_CXX_FLAGS "-g -O0 -Wall -fprofile-arcs -ftest-coverage") |
46 | + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage -lgcov") |
47 | + include(${CMAKE_SOURCE_DIR}/cmake/lcov.cmake) |
48 | +endif() |
49 | + |
50 | +enable_testing() |
51 | + |
52 | +add_subdirectory(storage-change-notifier) |
53 | +add_subdirectory(buteo-contact-client) |
54 | +add_subdirectory(google) |
55 | +add_subdirectory(accounts) |
56 | +add_subdirectory(tests) |
57 | + |
58 | +# uninstall target |
59 | +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" |
60 | + "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" |
61 | + IMMEDIATE @ONLY) |
62 | +add_custom_target(uninstall "${CMAKE_COMMAND}" |
63 | + -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") |
64 | |
65 | === added directory 'accounts' |
66 | === added file 'accounts/CMakeLists.txt' |
67 | --- accounts/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
68 | +++ accounts/CMakeLists.txt 2015-09-24 17:49:24 +0000 |
69 | @@ -0,0 +1,14 @@ |
70 | +set(SERVICE_FILES |
71 | + google-contacts.service |
72 | +) |
73 | +set(APPLICATION_FILES |
74 | + address-book-app.application |
75 | +) |
76 | + |
77 | +install(FILES ${SERVICE_FILES} |
78 | + DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/accounts/services |
79 | +) |
80 | + |
81 | +install(FILES ${APPLICATION_FILES} |
82 | + DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/accounts/applications |
83 | +) |
84 | |
85 | === added file 'accounts/address-book-app.application' |
86 | --- accounts/address-book-app.application 1970-01-01 00:00:00 +0000 |
87 | +++ accounts/address-book-app.application 2015-09-24 17:49:24 +0000 |
88 | @@ -0,0 +1,11 @@ |
89 | +<?xml version="1.0" encoding="UTF-8"?> |
90 | +<application id="address-book-app"> |
91 | + <description>Contacts</description> |
92 | + <desktop-entry>address-book-app.desktop</desktop-entry> |
93 | + |
94 | + <service-types> |
95 | + <service-type id="contacts"> |
96 | + <description>Synchronize your contacts</description> |
97 | + </service-type> |
98 | + </service-types> |
99 | +</application> |
100 | |
101 | === added file 'accounts/google-contacts.service' |
102 | --- accounts/google-contacts.service 1970-01-01 00:00:00 +0000 |
103 | +++ accounts/google-contacts.service 2015-09-24 17:49:24 +0000 |
104 | @@ -0,0 +1,6 @@ |
105 | +<?xml version="1.0" encoding="UTF-8"?> |
106 | +<service id="google-contacts"> |
107 | + <type>contacts</type> |
108 | + <name>Google Contacts</name> |
109 | + <provider>google</provider> |
110 | +</service> |
111 | |
112 | === added directory 'buteo-contact-client' |
113 | === added file 'buteo-contact-client/CMakeLists.txt' |
114 | --- buteo-contact-client/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
115 | +++ buteo-contact-client/CMakeLists.txt 2015-09-24 17:49:24 +0000 |
116 | @@ -0,0 +1,37 @@ |
117 | +project(buteo-contact-client) |
118 | + |
119 | +set(UBUNTU_CONTACTS_CLIENT ubuntu-contact-client) |
120 | +set(UBUNTU_CONTACTS_CLIENT_SRCS |
121 | + UAbstractRemoteSource.h |
122 | + UAbstractRemoteSource.cpp |
123 | + UAuth.cpp |
124 | + UAuth.h |
125 | + UContactsBackend.cpp |
126 | + UContactsBackend.h |
127 | + UContactsClient.cpp |
128 | + UContactsClient.h |
129 | + UContactsCustomDetail.cpp |
130 | + UContactsCustomDetail.h |
131 | +) |
132 | + |
133 | + |
134 | +add_library(${UBUNTU_CONTACTS_CLIENT} STATIC |
135 | + ${UBUNTU_CONTACTS_CLIENT_SRCS} |
136 | +) |
137 | + |
138 | +include_directories( |
139 | + ${CMAKE_BINARY_DIR} |
140 | + ${ACCOUNTS_INCLUDE_DIRS} |
141 | + ${BUTEOSYNCFW_INCLUDE_DIRS} |
142 | + ${LIBSIGNON_INCLUDE_DIRS} |
143 | +) |
144 | + |
145 | +target_link_libraries(${UBUNTU_CONTACTS_CLIENT} |
146 | + Qt5::Core |
147 | + Qt5::Network |
148 | + Qt5::DBus |
149 | + Qt5::Contacts |
150 | + ${ACCOUNTS_LIBRARIES} |
151 | + ${BUTEOSYNCFW_LIBRARIES} |
152 | + ${LIBSIGNON_LIBRARIES} |
153 | +) |
154 | |
155 | === added file 'buteo-contact-client/UAbstractRemoteSource.cpp' |
156 | --- buteo-contact-client/UAbstractRemoteSource.cpp 1970-01-01 00:00:00 +0000 |
157 | +++ buteo-contact-client/UAbstractRemoteSource.cpp 2015-09-24 17:49:24 +0000 |
158 | @@ -0,0 +1,178 @@ |
159 | +/* |
160 | + * This file is part of buteo-sync-plugins-contacts package |
161 | + * |
162 | + * Copyright (C) 2015 Canonical Ltd |
163 | + * |
164 | + * Contributors: Renato Araujo Oliveira Filho <renato.filho@canonical.com> |
165 | + * |
166 | + * This library is free software; you can redistribute it and/or |
167 | + * modify it under the terms of the GNU Lesser General Public License |
168 | + * version 2.1 as published by the Free Software Foundation. |
169 | + * |
170 | + * This library is distributed in the hope that it will be useful, but |
171 | + * WITHOUT ANY WARRANTY; without even the implied warranty of |
172 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
173 | + * Lesser General Public License for more details. |
174 | + * |
175 | + * You should have received a copy of the GNU Lesser General Public |
176 | + * License along with this library; if not, write to the Free Software |
177 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
178 | + * 02110-1301 USA |
179 | + * |
180 | + */ |
181 | + |
182 | +#include "UAbstractRemoteSource.h" |
183 | +#include "UContactsBackend.h" |
184 | + |
185 | +#include <QDebug> |
186 | + |
187 | +QTCONTACTS_USE_NAMESPACE |
188 | + |
189 | +class UContactsBackendBatchOperation |
190 | +{ |
191 | +public: |
192 | + enum BatchOperation { |
193 | + BATCH_UPDATE = 0, |
194 | + BATCH_DELETE, |
195 | + BATCH_CREATE, |
196 | + BATCH_NONE, |
197 | + }; |
198 | + |
199 | + UContactsBackendBatchOperation() |
200 | + : m_op(BATCH_NONE) |
201 | + { |
202 | + } |
203 | + |
204 | + UContactsBackendBatchOperation(BatchOperation op, const QContact &contact) |
205 | + : m_op(op), |
206 | + m_contact(contact) |
207 | + { |
208 | + } |
209 | + |
210 | + BatchOperation operation() const |
211 | + { |
212 | + return m_op; |
213 | + } |
214 | + |
215 | + QContact contact() const |
216 | + { |
217 | + return m_contact; |
218 | + } |
219 | + |
220 | + bool isValid() const |
221 | + { |
222 | + return (m_op != BATCH_NONE); |
223 | + } |
224 | + |
225 | +private: |
226 | + BatchOperation m_op; |
227 | + QContact m_contact; |
228 | +}; |
229 | + |
230 | +class UAbstractRemoteSourcePrivate |
231 | +{ |
232 | +public: |
233 | + UAbstractRemoteSourcePrivate() |
234 | + : m_batchMode(false) |
235 | + { |
236 | + } |
237 | + |
238 | + bool m_batchMode; |
239 | + QList<UContactsBackendBatchOperation> m_operations; |
240 | +}; |
241 | + |
242 | +UAbstractRemoteSource::UAbstractRemoteSource(QObject *parent) |
243 | + : QObject(parent), |
244 | + d_ptr(new UAbstractRemoteSourcePrivate) |
245 | +{ |
246 | +} |
247 | + |
248 | +UAbstractRemoteSource::~UAbstractRemoteSource() |
249 | +{ |
250 | +} |
251 | + |
252 | +void UAbstractRemoteSource::transaction() |
253 | +{ |
254 | + Q_D(UAbstractRemoteSource); |
255 | + |
256 | + d->m_batchMode = true; |
257 | +} |
258 | + |
259 | +bool UAbstractRemoteSource::commit() |
260 | +{ |
261 | + Q_D(UAbstractRemoteSource); |
262 | + |
263 | + if (d->m_operations.isEmpty()) { |
264 | + transactionCommited(QList<QContact>(), |
265 | + QList<QContact>(), |
266 | + QStringList(), |
267 | + QMap<QString, int>(), |
268 | + Sync::SYNC_DONE); |
269 | + return true; |
270 | + } |
271 | + |
272 | + QList<QContact> create; |
273 | + QList<QContact> update; |
274 | + QList<QContact> remove; |
275 | + |
276 | + foreach(const UContactsBackendBatchOperation &op, d->m_operations) { |
277 | + switch(op.operation()) { |
278 | + case UContactsBackendBatchOperation::BATCH_CREATE: |
279 | + create << op.contact(); |
280 | + break; |
281 | + case UContactsBackendBatchOperation::BATCH_UPDATE: |
282 | + update << op.contact(); |
283 | + break; |
284 | + case UContactsBackendBatchOperation::BATCH_DELETE: |
285 | + remove << op.contact(); |
286 | + break; |
287 | + default: |
288 | + qWarning() << "Invalid operation"; |
289 | + } |
290 | + } |
291 | + |
292 | + batch(create, update, remove); |
293 | + d->m_operations.clear(); |
294 | + d->m_batchMode = false; |
295 | + return true; |
296 | +} |
297 | + |
298 | +bool UAbstractRemoteSource::rollback() |
299 | +{ |
300 | + Q_D(UAbstractRemoteSource); |
301 | + |
302 | + d->m_operations.clear(); |
303 | + d->m_batchMode = false; |
304 | + return true; |
305 | +} |
306 | + |
307 | +void UAbstractRemoteSource::saveContacts(const QList<QContact> &contacts) |
308 | +{ |
309 | + Q_D(UAbstractRemoteSource); |
310 | + |
311 | + if (d->m_batchMode) { |
312 | + foreach(const QContact &contact, contacts) { |
313 | + UContactsBackendBatchOperation::BatchOperation op; |
314 | + QString remoteId = UContactsBackend::getRemoteId(contact); |
315 | + op = remoteId.isEmpty() ? UContactsBackendBatchOperation::BATCH_CREATE : |
316 | + UContactsBackendBatchOperation::BATCH_UPDATE; |
317 | + d->m_operations << UContactsBackendBatchOperation(op, contact); |
318 | + } |
319 | + } else { |
320 | + saveContactsNonBatch(contacts); |
321 | + } |
322 | +} |
323 | + |
324 | +void UAbstractRemoteSource::removeContacts(const QList<QContact> &contacts) |
325 | +{ |
326 | + Q_D(UAbstractRemoteSource); |
327 | + |
328 | + if (d->m_batchMode) { |
329 | + foreach(const QContact &contact, contacts) { |
330 | + d->m_operations << UContactsBackendBatchOperation(UContactsBackendBatchOperation::BATCH_DELETE, |
331 | + contact); |
332 | + } |
333 | + } else { |
334 | + removeContactsNonBatch(contacts); |
335 | + } |
336 | +} |
337 | |
338 | === added file 'buteo-contact-client/UAbstractRemoteSource.h' |
339 | --- buteo-contact-client/UAbstractRemoteSource.h 1970-01-01 00:00:00 +0000 |
340 | +++ buteo-contact-client/UAbstractRemoteSource.h 2015-09-24 17:49:24 +0000 |
341 | @@ -0,0 +1,125 @@ |
342 | +/* |
343 | + * This file is part of buteo-sync-plugins-contacts package |
344 | + * |
345 | + * Copyright (C) 2015 Canonical Ltd |
346 | + * |
347 | + * Contributors: Renato Araujo Oliveira Filho <renato.filho@canonical.com> |
348 | + * |
349 | + * This library is free software; you can redistribute it and/or |
350 | + * modify it under the terms of the GNU Lesser General Public License |
351 | + * version 2.1 as published by the Free Software Foundation. |
352 | + * |
353 | + * This library is distributed in the hope that it will be useful, but |
354 | + * WITHOUT ANY WARRANTY; without even the implied warranty of |
355 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
356 | + * Lesser General Public License for more details. |
357 | + * |
358 | + * You should have received a copy of the GNU Lesser General Public |
359 | + * License along with this library; if not, write to the Free Software |
360 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
361 | + * 02110-1301 USA |
362 | + * |
363 | + */ |
364 | + |
365 | +#ifndef UABSTRACTREMOTESOURCE_H |
366 | +#define UABSTRACTREMOTESOURCE_H |
367 | + |
368 | +#include <QObject> |
369 | +#include <QDateTime> |
370 | +#include <QVariantMap> |
371 | + |
372 | +#include <QtContacts/QContact> |
373 | + |
374 | +#include <SyncCommonDefs.h> |
375 | + |
376 | +class UAbstractRemoteSourcePrivate; |
377 | + |
378 | +class UAbstractRemoteSource : public QObject |
379 | +{ |
380 | + Q_OBJECT |
381 | + Q_DECLARE_PRIVATE(UAbstractRemoteSource) |
382 | + |
383 | +public: |
384 | + |
385 | + UAbstractRemoteSource(QObject *parent = 0); |
386 | + ~UAbstractRemoteSource(); |
387 | + |
388 | + virtual void abort() = 0; |
389 | + virtual bool init(const QVariantMap &properties) = 0; |
390 | + virtual void fetchContacts(const QDateTime &since, bool includeDeleted, bool fetchAvatar = true) = 0; |
391 | + |
392 | + /*! |
393 | + * \brief Begins a transaction on the remote database. |
394 | + */ |
395 | + void transaction(); |
396 | + |
397 | + /*! |
398 | + * \brief Commits a transaction to the remote database. |
399 | + */ |
400 | + bool commit(); |
401 | + |
402 | + /*! |
403 | + * \brief Rolls back a transaction on the remote database. |
404 | + */ |
405 | + bool rollback(); |
406 | + |
407 | + virtual void saveContacts(const QList<QtContacts::QContact> &contacts); |
408 | + virtual void removeContacts(const QList<QtContacts::QContact> &contacts); |
409 | + |
410 | +signals: |
411 | + void contactsFetched(const QList<QtContacts::QContact> &contacts, |
412 | + Sync::SyncStatus status, |
413 | + qreal progress); |
414 | + |
415 | + /*! |
416 | + * \brief This signal is emitted, when a remote contact is created |
417 | + * \param contacts A list of created contacts |
418 | + * \param status The operation status |
419 | + */ |
420 | + void contactsCreated(const QList<QtContacts::QContact> &contacts, Sync::SyncStatus status); |
421 | + |
422 | + /*! |
423 | + * \brief This signal is emitted, when a remote contact is changed |
424 | + * \param contacts A list of changed contacts |
425 | + * \param status The operation status |
426 | + */ |
427 | + void contactsChanged(const QList<QtContacts::QContact> &contacts, Sync::SyncStatus status); |
428 | + |
429 | + /*! |
430 | + * \brief This signal is emitted, when a remote contact is removed |
431 | + * \param ids A list with remoteId of removed contacts |
432 | + * \param status The operation status |
433 | + */ |
434 | + void contactsRemoved(const QStringList &ids, Sync::SyncStatus status); |
435 | + |
436 | + /*! |
437 | + * \brief This signal is emitted, when a batch operation finishes |
438 | + * \param createdContacts A list of created contacts |
439 | + * \param updatedContacts A list of updated contacts |
440 | + * \param removedContacts A list with remoteId of removed contacts |
441 | + * \param errorMap A map with the list of errors found during the batch operation |
442 | + * the key value contains the contact local id and the value is a int based |
443 | + * on QContactManager::Error enum |
444 | + * \param status The operation status |
445 | + */ |
446 | + void transactionCommited(const QList<QtContacts::QContact> &createdContacts, |
447 | + const QList<QtContacts::QContact> &updatedContacts, |
448 | + const QStringList &removedContacts, |
449 | + const QMap<QString, int> &errorMap, |
450 | + Sync::SyncStatus status); |
451 | + |
452 | +protected: |
453 | + virtual void batch(const QList<QtContacts::QContact> &contactsToCreate, |
454 | + const QList<QtContacts::QContact> &contactsToUpdate, |
455 | + const QList<QtContacts::QContact> &contactsToRemove) = 0; |
456 | + |
457 | + virtual void saveContactsNonBatch(const QList<QtContacts::QContact> contacts) = 0; |
458 | + virtual void removeContactsNonBatch(const QList<QtContacts::QContact> contacts) = 0; |
459 | + |
460 | +private: |
461 | + QScopedPointer<UAbstractRemoteSourcePrivate> d_ptr; |
462 | +}; |
463 | + |
464 | +Q_DECLARE_METATYPE(QList<QtContacts::QContact>) |
465 | + |
466 | +#endif // UABSTRACTREMOTESOURCE_H |
467 | |
468 | === added file 'buteo-contact-client/UAuth.cpp' |
469 | --- buteo-contact-client/UAuth.cpp 1970-01-01 00:00:00 +0000 |
470 | +++ buteo-contact-client/UAuth.cpp 2015-09-24 17:49:24 +0000 |
471 | @@ -0,0 +1,181 @@ |
472 | +/* |
473 | + * This file is part of buteo-sync-plugins-contacts package |
474 | + * |
475 | + * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). |
476 | + * 2015 Canonical Ltd |
477 | + * |
478 | + * Contributors: Sateesh Kavuri <sateesh.kavuri@gmail.com> |
479 | + * Mani Chandrasekar <maninc@gmail.com> |
480 | + * Renato Araujo Oliveira Filho <renato.filho@canonical.com> |
481 | + * |
482 | + * This library is free software; you can redistribute it and/or |
483 | + * modify it under the terms of the GNU Lesser General Public License |
484 | + * version 2.1 as published by the Free Software Foundation. |
485 | + * |
486 | + * This library is distributed in the hope that it will be useful, but |
487 | + * WITHOUT ANY WARRANTY; without even the implied warranty of |
488 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
489 | + * Lesser General Public License for more details. |
490 | + * |
491 | + * You should have received a copy of the GNU Lesser General Public |
492 | + * License along with this library; if not, write to the Free Software |
493 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
494 | + * 02110-1301 USA |
495 | + * |
496 | + */ |
497 | + |
498 | +#include "config.h" |
499 | +#include "UAuth.h" |
500 | + |
501 | +#include <QVariantMap> |
502 | +#include <QTextStream> |
503 | +#include <QFile> |
504 | +#include <QStringList> |
505 | +#include <QDebug> |
506 | + |
507 | +#include <Accounts/AccountService> |
508 | + |
509 | +#include <ProfileEngineDefs.h> |
510 | +#include <LogMacros.h> |
511 | + |
512 | +using namespace Accounts; |
513 | +using namespace SignOn; |
514 | + |
515 | +class UAuthPrivate |
516 | +{ |
517 | +public: |
518 | + UAuthPrivate() {} |
519 | + ~UAuthPrivate() {} |
520 | + |
521 | + QPointer<Accounts::Manager> mAccountManager; |
522 | + QPointer<SignOn::Identity> mIdentity; |
523 | + QPointer<SignOn::AuthSession> mSession; |
524 | + QPointer<Accounts::Account> mAccount; |
525 | + QString mServiceName; |
526 | +}; |
527 | + |
528 | +UAuth::UAuth(QObject *parent) |
529 | + : QObject(parent), |
530 | + d_ptr(new UAuthPrivate) |
531 | +{ |
532 | +} |
533 | + |
534 | +UAuth::~UAuth() |
535 | +{ |
536 | +} |
537 | + |
538 | +bool |
539 | +UAuth::init(const quint32 accountId, const QString serviceName) |
540 | +{ |
541 | + Q_D(UAuth); |
542 | + |
543 | + d->mServiceName = serviceName; |
544 | + if (d->mAccountManager && d_ptr->mAccount) { |
545 | + LOG_DEBUG("GAuth already initialized"); |
546 | + return false; |
547 | + } |
548 | + |
549 | + if (!d->mAccountManager) { |
550 | + d->mAccountManager = new Accounts::Manager(); |
551 | + if (d->mAccountManager == NULL) { |
552 | + LOG_DEBUG("Account manager is not created... Cannot authenticate"); |
553 | + return false; |
554 | + } |
555 | + } |
556 | + |
557 | + if (!d->mAccount) { |
558 | + d->mAccount = Accounts::Account::fromId(d->mAccountManager.data(), accountId, this); |
559 | + if (d->mAccount == NULL) { |
560 | + LOG_DEBUG("Account is not created... Cannot authenticate"); |
561 | + return false; |
562 | + } |
563 | + mDisplayName = d->mAccount->displayName(); |
564 | + } |
565 | + |
566 | + return true; |
567 | +} |
568 | + |
569 | +void |
570 | +UAuth::sessionResponse(const SessionData &sessionData) |
571 | +{ |
572 | + SignOn::AuthSession *session = qobject_cast<SignOn::AuthSession*>(sender()); |
573 | + Q_ASSERT(session); |
574 | + session->disconnect(this); |
575 | + |
576 | + mToken = sessionData.getProperty(QStringLiteral("AccessToken")).toString(); |
577 | + LOG_DEBUG("Authenticated !!!"); |
578 | + |
579 | + emit success(); |
580 | +} |
581 | + |
582 | +bool |
583 | +UAuth::authenticate() |
584 | +{ |
585 | + Q_D(UAuth); |
586 | + if (d->mSession) { |
587 | + LOG_WARNING(QString("error: Account %1 Authenticate already requested") |
588 | + .arg(d->mAccount->displayName())); |
589 | + return true; |
590 | + } |
591 | + |
592 | + Accounts::Service srv(d->mAccountManager->service(d->mServiceName)); |
593 | + if (!srv.isValid()) { |
594 | + LOG_WARNING(QString("error: Service [%1] not found for account [%2].") |
595 | + .arg(d->mServiceName) |
596 | + .arg(d->mAccount->displayName())); |
597 | + return false; |
598 | + } |
599 | + d->mAccount->selectService(srv); |
600 | + |
601 | + Accounts::AccountService *accSrv = new Accounts::AccountService(d->mAccount, srv); |
602 | + if (!accSrv) { |
603 | + LOG_WARNING(QString("error: Account %1 has no valid account service") |
604 | + .arg(d->mAccount->displayName())); |
605 | + return false; |
606 | + } |
607 | + if (!accSrv->isEnabled()) { |
608 | + LOG_WARNING(QString("error: Service %1 not enabled for account %2.") |
609 | + .arg(d->mServiceName) |
610 | + .arg(d->mAccount->displayName())); |
611 | + return false; |
612 | + } |
613 | + |
614 | + AuthData authData = accSrv->authData(); |
615 | + d->mIdentity = SignOn::Identity::existingIdentity(authData.credentialsId()); |
616 | + if (!d->mIdentity) { |
617 | + LOG_WARNING(QString("error: Account %1 has no valid credentials") |
618 | + .arg(d->mAccount->displayName())); |
619 | + return false; |
620 | + } |
621 | + |
622 | + d->mSession = d->mIdentity->createSession(authData.method()); |
623 | + if (!d->mSession) { |
624 | + LOG_WARNING(QString("error: could not create signon session for Google account %1") |
625 | + .arg(d->mAccount->displayName())); |
626 | + accSrv->deleteLater(); |
627 | + return false; |
628 | + } |
629 | + connect(d->mSession.data(),SIGNAL(response(SignOn::SessionData)), |
630 | + SLOT(sessionResponse(SignOn::SessionData)), Qt::QueuedConnection); |
631 | + connect(d->mSession.data(), SIGNAL(error(SignOn::Error)), |
632 | + SLOT(error(SignOn::Error)), Qt::QueuedConnection); |
633 | + |
634 | + QVariantMap signonSessionData = authData.parameters(); |
635 | + signonSessionData.insert("UiPolicy", SignOn::NoUserInteractionPolicy); |
636 | + d->mSession->process(signonSessionData, authData.mechanism()); |
637 | + accSrv->deleteLater(); |
638 | + return true; |
639 | +} |
640 | + |
641 | +void UAuth::credentialsStored(const quint32 id) |
642 | +{ |
643 | + Q_D(UAuth); |
644 | + d->mAccount->setCredentialsId(id); |
645 | + d->mAccount->sync(); |
646 | +} |
647 | + |
648 | +void UAuth::error(const SignOn::Error & error) |
649 | +{ |
650 | + LOG_WARNING("LOGIN ERROR:" << error.message()); |
651 | + emit failed(); |
652 | +} |
653 | |
654 | === added file 'buteo-contact-client/UAuth.h' |
655 | --- buteo-contact-client/UAuth.h 1970-01-01 00:00:00 +0000 |
656 | +++ buteo-contact-client/UAuth.h 2015-09-24 17:49:24 +0000 |
657 | @@ -0,0 +1,72 @@ |
658 | +/* |
659 | + * This file is part of buteo-sync-plugins-contacts package |
660 | + * |
661 | + * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). |
662 | + * 2015 Canonical Ltd |
663 | + * |
664 | + * Contributors: Sateesh Kavuri <sateesh.kavuri@gmail.com> |
665 | + * Mani Chandrasekar <maninc@gmail.com> |
666 | + * Renato Araujo Oliveira Filho <renato.filho@canonical.com> |
667 | + * |
668 | + * This library is free software; you can redistribute it and/or |
669 | + * modify it under the terms of the GNU Lesser General Public License |
670 | + * version 2.1 as published by the Free Software Foundation. |
671 | + * |
672 | + * This library is distributed in the hope that it will be useful, but |
673 | + * WITHOUT ANY WARRANTY; without even the implied warranty of |
674 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
675 | + * Lesser General Public License for more details. |
676 | + * |
677 | + * You should have received a copy of the GNU Lesser General Public |
678 | + * License along with this library; if not, write to the Free Software |
679 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
680 | + * 02110-1301 USA |
681 | + * |
682 | + */ |
683 | + |
684 | +#ifndef UAUTH_H |
685 | +#define UAUTH_H |
686 | + |
687 | +#include <QObject> |
688 | +#include <QScopedPointer> |
689 | + |
690 | +#include <SignOn/AuthService> |
691 | +#include <SignOn/Identity> |
692 | + |
693 | +#include <Accounts/Account> |
694 | +#include <Accounts/Manager> |
695 | + |
696 | +class UAuthPrivate; |
697 | + |
698 | +class UAuth : public QObject |
699 | +{ |
700 | + Q_OBJECT |
701 | + Q_DECLARE_PRIVATE(UAuth) |
702 | +public: |
703 | + explicit UAuth(QObject *parent = 0); |
704 | + ~UAuth(); |
705 | + |
706 | + virtual bool authenticate(); |
707 | + virtual bool init(const quint32 accountId, const QString serviceName); |
708 | + |
709 | + inline QString accountDisplayName() const { return mDisplayName; } |
710 | + inline QString token() const { return mToken; } |
711 | + |
712 | +signals: |
713 | + void success(); |
714 | + void failed(); |
715 | + |
716 | +protected: |
717 | + QString mToken; |
718 | + QString mDisplayName; |
719 | + |
720 | +private: |
721 | + QScopedPointer<UAuthPrivate> d_ptr; |
722 | + |
723 | +private slots: |
724 | + void credentialsStored(const quint32); |
725 | + void error(const SignOn::Error &); |
726 | + void sessionResponse(const SignOn::SessionData &); |
727 | +}; |
728 | + |
729 | +#endif // UAUTH_H |
730 | |
731 | === added file 'buteo-contact-client/UContactsBackend.cpp' |
732 | --- buteo-contact-client/UContactsBackend.cpp 1970-01-01 00:00:00 +0000 |
733 | +++ buteo-contact-client/UContactsBackend.cpp 2015-09-24 17:49:24 +0000 |
734 | @@ -0,0 +1,572 @@ |
735 | +/* |
736 | + * This file is part of buteo-sync-plugins-contacts package |
737 | + * |
738 | + * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). |
739 | + * 2015 Canonical Ltd |
740 | + * |
741 | + * Contributors: Sateesh Kavuri <sateesh.kavuri@gmail.com> |
742 | + * Mani Chandrasekar <maninc@gmail.com> |
743 | + * Renato Araujo Oliveira Filho <renato.filho@canonical.com> |
744 | + * |
745 | + * This library is free software; you can redistribute it and/or |
746 | + * modify it under the terms of the GNU Lesser General Public License |
747 | + * version 2.1 as published by the Free Software Foundation. |
748 | + * |
749 | + * This library is distributed in the hope that it will be useful, but |
750 | + * WITHOUT ANY WARRANTY; without even the implied warranty of |
751 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
752 | + * Lesser General Public License for more details. |
753 | + * |
754 | + * You should have received a copy of the GNU Lesser General Public |
755 | + * License along with this library; if not, write to the Free Software |
756 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
757 | + * 02110-1301 USA |
758 | + * |
759 | + */ |
760 | + |
761 | +#include "config.h" |
762 | +#include "UContactsBackend.h" |
763 | +#include "UContactsCustomDetail.h" |
764 | + |
765 | +#include <LogMacros.h> |
766 | + |
767 | +#include <QContactTimestamp> |
768 | +#include <QContactIdFilter> |
769 | +#include <QContactIntersectionFilter> |
770 | +#include <QContactSyncTarget> |
771 | +#include <QContactDetailFilter> |
772 | +#include <QContactGuid> |
773 | +#include <QContactDisplayLabel> |
774 | +#include <QContactExtendedDetail> |
775 | +#include <QContactSyncTarget> |
776 | + |
777 | +#include <QBuffer> |
778 | +#include <QSet> |
779 | +#include <QHash> |
780 | + |
781 | +#include <QDBusInterface> |
782 | +#include <QDBusReply> |
783 | + |
784 | +static const QString CPIM_SERVICE_NAME ("com.canonical.pim"); |
785 | +static const QString CPIM_ADDRESSBOOK_OBJECT_PATH ("/com/canonical/pim/AddressBook"); |
786 | +static const QString CPIM_ADDRESSBOOK_IFACE_NAME ("com.canonical.pim.AddressBook"); |
787 | + |
788 | +UContactsBackend::UContactsBackend(const QString &managerName, QObject* parent) |
789 | + : QObject (parent) |
790 | +{ |
791 | + QMap<QString, QString> parameters; |
792 | + parameters.insert("show-invisible", "true"); |
793 | + iMgr = new QContactManager(managerName, parameters); |
794 | + FUNCTION_CALL_TRACE; |
795 | +} |
796 | + |
797 | +UContactsBackend::~UContactsBackend() |
798 | +{ |
799 | + FUNCTION_CALL_TRACE; |
800 | + |
801 | + delete iMgr; |
802 | + iMgr = NULL; |
803 | +} |
804 | + |
805 | +bool |
806 | +UContactsBackend::init(uint syncAccount, const QString &syncTarget) |
807 | +{ |
808 | + FUNCTION_CALL_TRACE; |
809 | + |
810 | + // create address book it it does not exists |
811 | + // check if the source already exists |
812 | + QContactDetailFilter filter; |
813 | + filter.setDetailType(QContactDetail::TypeType, QContactType::FieldType); |
814 | + filter.setValue(QContactType::TypeGroup); |
815 | + |
816 | + QList<QContact> sources = iMgr->contacts(filter); |
817 | + Q_FOREACH(const QContact &contact, sources) { |
818 | + QContactExtendedDetail exd = UContactsCustomDetail::getCustomField(contact, |
819 | + "ACCOUNT-ID"); |
820 | + if (!exd.isEmpty() && (exd.data().toUInt() == syncAccount)) { |
821 | + mSyncTargetId = contact.detail<QContactGuid>().guid(); |
822 | + reloadCache(); |
823 | + return true; |
824 | + } |
825 | + } |
826 | + |
827 | + // memory/mock manager does not support syncTarget |
828 | + if (iMgr->managerName() != "mock") { |
829 | + // create a new source if necessary |
830 | + QContact contact; |
831 | + contact.setType(QContactType::TypeGroup); |
832 | + |
833 | + QContactDisplayLabel label; |
834 | + label.setLabel(syncTarget); |
835 | + contact.saveDetail(&label); |
836 | + |
837 | + // set the new source as default |
838 | + QContactExtendedDetail isDefault; |
839 | + isDefault.setName("IS-PRIMARY"); |
840 | + isDefault.setData(true); |
841 | + contact.saveDetail(&isDefault); |
842 | + |
843 | + // Link source with account |
844 | + QContactExtendedDetail accountId; |
845 | + accountId.setName("ACCOUNT-ID"); |
846 | + accountId.setData(syncAccount); |
847 | + contact.saveDetail(&accountId); |
848 | + |
849 | + if (!iMgr->saveContact(&contact)) { |
850 | + qWarning() << "Fail to create contact source:" << syncTarget; |
851 | + return false; |
852 | + } |
853 | + |
854 | + mSyncTargetId = contact.detail<QContactGuid>().guid(); |
855 | + } |
856 | + |
857 | + return true; |
858 | +} |
859 | + |
860 | +bool |
861 | +UContactsBackend::uninit() |
862 | +{ |
863 | + FUNCTION_CALL_TRACE; |
864 | + mRemoteIdToLocalId.clear(); |
865 | + |
866 | + return true; |
867 | +} |
868 | + |
869 | +QList<QContactId> |
870 | +UContactsBackend::getAllContactIds() |
871 | +{ |
872 | + FUNCTION_CALL_TRACE; |
873 | + Q_ASSERT (iMgr); |
874 | + return iMgr->contactIds(getSyncTargetFilter()); |
875 | +} |
876 | + |
877 | +RemoteToLocalIdMap |
878 | +UContactsBackend::getAllNewContactIds(const QDateTime &aTimeStamp) |
879 | +{ |
880 | + FUNCTION_CALL_TRACE; |
881 | + LOG_DEBUG("Retrieve New Contacts Since " << aTimeStamp); |
882 | + |
883 | + RemoteToLocalIdMap idList; |
884 | + const QContactChangeLogFilter::EventType eventType = |
885 | + QContactChangeLogFilter::EventAdded; |
886 | + |
887 | + getSpecifiedContactIds(eventType, aTimeStamp, &idList); |
888 | + |
889 | + return idList; |
890 | +} |
891 | + |
892 | +RemoteToLocalIdMap |
893 | +UContactsBackend::getAllModifiedContactIds(const QDateTime &aTimeStamp) |
894 | +{ |
895 | + |
896 | + FUNCTION_CALL_TRACE; |
897 | + |
898 | + LOG_DEBUG("Retrieve Modified Contacts Since " << aTimeStamp); |
899 | + |
900 | + RemoteToLocalIdMap idList; |
901 | + const QContactChangeLogFilter::EventType eventType = |
902 | + QContactChangeLogFilter::EventChanged; |
903 | + |
904 | + getSpecifiedContactIds(eventType, aTimeStamp, &idList); |
905 | + |
906 | + return idList; |
907 | +} |
908 | + |
909 | +RemoteToLocalIdMap |
910 | +UContactsBackend::getAllDeletedContactIds(const QDateTime &aTimeStamp) |
911 | +{ |
912 | + FUNCTION_CALL_TRACE; |
913 | + LOG_DEBUG("Retrieve Deleted Contacts Since " << aTimeStamp); |
914 | + |
915 | + RemoteToLocalIdMap idList; |
916 | + const QContactChangeLogFilter::EventType eventType = |
917 | + QContactChangeLogFilter::EventRemoved; |
918 | + |
919 | + getSpecifiedContactIds(eventType, aTimeStamp, &idList); |
920 | + |
921 | + return idList; |
922 | +} |
923 | + |
924 | +bool |
925 | +UContactsBackend::addContacts(QList<QContact>& aContactList, |
926 | + QMap<int, UContactsStatus> *aStatusMap) |
927 | +{ |
928 | + FUNCTION_CALL_TRACE; |
929 | + Q_ASSERT(iMgr); |
930 | + Q_ASSERT(aStatusMap); |
931 | + |
932 | + QMap<int, QContactManager::Error> errorMap; |
933 | + |
934 | + // Check if contact already exists if it exists set the contact id |
935 | + // to cause an update instead of create a new one |
936 | + for(int i=0; i < aContactList.size(); i++) { |
937 | + QContact &c = aContactList[i]; |
938 | + QString remoteId = getRemoteId(c); |
939 | + QContactId id = entryExists(remoteId); |
940 | + if (!id.isNull()) { |
941 | + c.setId(id); |
942 | + } else { |
943 | + // make sure that all contacts retrieved are saved on the correct sync target |
944 | + QContactSyncTarget syncTarget = c.detail<QContactSyncTarget>(); |
945 | + syncTarget.setSyncTarget(syncTargetId()); |
946 | + c.saveDetail(&syncTarget); |
947 | + } |
948 | + |
949 | + // remove guid field if it exists |
950 | + QContactGuid guid = c.detail<QContactGuid>(); |
951 | + if (!guid.isEmpty()) { |
952 | + c.removeDetail(&guid); |
953 | + } |
954 | + } |
955 | + |
956 | + bool retVal = iMgr->saveContacts(&aContactList, &errorMap); |
957 | + if (!retVal) { |
958 | + LOG_WARNING( "Errors reported while saving contacts:" << iMgr->error() ); |
959 | + } |
960 | + |
961 | + // QContactManager will populate errorMap only for errors, but we use this as a status map, |
962 | + // so populate NoError if there's no error. |
963 | + for (int i = 0; i < aContactList.size(); i++) |
964 | + { |
965 | + UContactsStatus status; |
966 | + status.id = i; |
967 | + if (!errorMap.contains(i)) { |
968 | + status.errorCode = QContactManager::NoError; |
969 | + |
970 | + // update remote id map |
971 | + const QContact &c = aContactList.at(i); |
972 | + QString remoteId = getRemoteId(c); |
973 | + mRemoteIdToLocalId.insert(remoteId, c.id()); |
974 | + } else { |
975 | + LOG_WARNING("Contact with id " << aContactList.at(i).id() << " and index " << i <<" is in error"); |
976 | + status.errorCode = errorMap.value(i); |
977 | + } |
978 | + aStatusMap->insert(i, status); |
979 | + } |
980 | + |
981 | + return retVal; |
982 | +} |
983 | + |
984 | +QMap<int,UContactsStatus> |
985 | +UContactsBackend::modifyContacts(QList<QContact> *aContactList) |
986 | +{ |
987 | + FUNCTION_CALL_TRACE; |
988 | + |
989 | + Q_ASSERT (iMgr); |
990 | + UContactsStatus status; |
991 | + |
992 | + QMap<int,QContactManager::Error> errors; |
993 | + QMap<int,UContactsStatus> statusMap; |
994 | + |
995 | + // WORKAROUND: Our backend uses GUid as contact id due problems with contact id serialization |
996 | + // we can not use this field |
997 | + for (int i = 0; i < aContactList->size(); i++) { |
998 | + QContact &newContact = (*aContactList)[i]; |
999 | + QString remoteId = getRemoteId(newContact); |
1000 | + |
1001 | + // if the contact was created the remoteId will not exists on local database |
1002 | + QContactId localId = entryExists(remoteId); |
1003 | + |
1004 | + // nt this case we should use the guid stored on contact |
1005 | + QContactGuid guid = newContact.detail<QContactGuid>(); |
1006 | + |
1007 | + if (localId.isNull() && !guid.isEmpty()) { |
1008 | + // try the guid (should contains the local id) field |
1009 | + localId = QContactId::fromString(guid.guid()); |
1010 | + } |
1011 | + newContact.setId(localId); |
1012 | + newContact.removeDetail(&guid); |
1013 | + } |
1014 | + |
1015 | + if(iMgr->saveContacts(aContactList , &errors)) { |
1016 | + LOG_DEBUG("Batch Modification of Contacts Succeeded"); |
1017 | + } else { |
1018 | + LOG_DEBUG("Batch Modification of Contacts Failed"); |
1019 | + } |
1020 | + |
1021 | + // QContactManager will populate errorMap only for errors, but we use this as a status map, |
1022 | + // so populate NoError if there's no error. |
1023 | + // TODO QContactManager populates indices from the aContactList, but we populate keys, is this OK? |
1024 | + for (int i = 0; i < aContactList->size(); i++) { |
1025 | + const QContact &c = aContactList->at(i); |
1026 | + QContactId contactId = c.id(); |
1027 | + if( !errors.contains(i) ) { |
1028 | + LOG_DEBUG("No error for contact with id " << contactId << " and index " << i); |
1029 | + status.errorCode = QContactManager::NoError; |
1030 | + statusMap.insert(i, status); |
1031 | + |
1032 | + // update remote it map |
1033 | + QString oldRemoteId = mRemoteIdToLocalId.key(c.id()); |
1034 | + mRemoteIdToLocalId.remove(oldRemoteId); |
1035 | + |
1036 | + QString remoteId = getRemoteId(c); |
1037 | + mRemoteIdToLocalId.insert(remoteId, c.id()); |
1038 | + } else { |
1039 | + LOG_DEBUG("contact with id " << contactId << " and index " << i <<" is in error"); |
1040 | + QContactManager::Error errorCode = errors.value(i); |
1041 | + status.errorCode = errorCode; |
1042 | + statusMap.insert(i, status); |
1043 | + } |
1044 | + } |
1045 | + return statusMap; |
1046 | +} |
1047 | + |
1048 | +QMap<int, UContactsStatus> |
1049 | +UContactsBackend::deleteContacts(const QStringList &aContactIDList) |
1050 | +{ |
1051 | + FUNCTION_CALL_TRACE; |
1052 | + |
1053 | + QList<QContactId> qContactIdList; |
1054 | + foreach (QString id, aContactIDList) { |
1055 | + qContactIdList.append(QContactId::fromString(id)); |
1056 | + } |
1057 | + |
1058 | + return deleteContacts(qContactIdList); |
1059 | +} |
1060 | + |
1061 | +QMap<int, UContactsStatus> |
1062 | +UContactsBackend::deleteContacts(const QList<QContactId> &aContactIDList) { |
1063 | + FUNCTION_CALL_TRACE; |
1064 | + |
1065 | + Q_ASSERT (iMgr); |
1066 | + UContactsStatus status; |
1067 | + QMap<int, QContactManager::Error> errors; |
1068 | + QMap<int, UContactsStatus> statusMap; |
1069 | + |
1070 | + if(iMgr->removeContacts(aContactIDList , &errors)) { |
1071 | + LOG_DEBUG("Successfully Removed all contacts "); |
1072 | + } |
1073 | + else { |
1074 | + LOG_WARNING("Failed Removing Contacts"); |
1075 | + } |
1076 | + |
1077 | + // QContactManager will populate errorMap only for errors, but we use this as a status map, |
1078 | + // so populate NoError if there's no error. |
1079 | + for (int i = 0; i < aContactIDList.size(); i++) { |
1080 | + const QContactId &contactId = aContactIDList.at(i); |
1081 | + if( !errors.contains(i) ) |
1082 | + { |
1083 | + LOG_DEBUG("No error for contact with id " << contactId << " and index " << i); |
1084 | + status.errorCode = QContactManager::NoError; |
1085 | + statusMap.insert(i, status); |
1086 | + |
1087 | + // remove from remote id map |
1088 | + QString remoteId = mRemoteIdToLocalId.key(contactId); |
1089 | + mRemoteIdToLocalId.remove(remoteId); |
1090 | + } |
1091 | + else |
1092 | + { |
1093 | + LOG_DEBUG("contact with id " << contactId << " and index " << i <<" is in error"); |
1094 | + QContactManager::Error errorCode = errors.value(i); |
1095 | + status.errorCode = errorCode; |
1096 | + statusMap.insert(i, status); |
1097 | + } |
1098 | + } |
1099 | + |
1100 | + return statusMap; |
1101 | +} |
1102 | + |
1103 | + |
1104 | +void |
1105 | +UContactsBackend::getSpecifiedContactIds(const QContactChangeLogFilter::EventType aEventType, |
1106 | + const QDateTime& aTimeStamp, |
1107 | + RemoteToLocalIdMap *aIdList) |
1108 | +{ |
1109 | + FUNCTION_CALL_TRACE; |
1110 | + Q_ASSERT(aIdList); |
1111 | + |
1112 | + QList<QContactId> localIdList; |
1113 | + QContactChangeLogFilter filter(aEventType); |
1114 | + filter.setSince(aTimeStamp); |
1115 | + |
1116 | + localIdList = iMgr->contactIds(filter & getSyncTargetFilter()); |
1117 | + LOG_DEBUG("Local ID added = " << localIdList.size() << " Datetime from when this " << aTimeStamp.toString()); |
1118 | + // Filter out ids for items that were added after the specified time. |
1119 | + if (aEventType != QContactChangeLogFilter::EventAdded) |
1120 | + { |
1121 | + filter.setEventType(QContactChangeLogFilter::EventAdded); |
1122 | + QList<QContactId> addedList = iMgr->contactIds(filter & getSyncTargetFilter()); |
1123 | + foreach (const QContactId &id, addedList) |
1124 | + { |
1125 | + localIdList.removeAll(id); |
1126 | + } |
1127 | + } |
1128 | + |
1129 | + // This is a defensive procedure to prevent duplicate items being sent. |
1130 | + // QSet does not allow duplicates, thus transforming QList to QSet and back |
1131 | + // again will remove any duplicate items in the original QList. |
1132 | + int originalIdCount = localIdList.size(); |
1133 | + QSet<QContactId> idSet = localIdList.toSet(); |
1134 | + int idCountAfterDupRemoval = idSet.size(); |
1135 | + |
1136 | + LOG_DEBUG("Item IDs found (returned / incl. duplicates): " << idCountAfterDupRemoval << "/" << originalIdCount); |
1137 | + if (originalIdCount != idCountAfterDupRemoval) { |
1138 | + LOG_WARNING("Contacts backend returned duplicate items for requested list"); |
1139 | + LOG_WARNING("Duplicate item IDs have been removed"); |
1140 | + } // no else |
1141 | + |
1142 | + localIdList = idSet.toList(); |
1143 | + |
1144 | + QContactFetchHint remoteIdHint; |
1145 | + QList <QContactDetail::DetailType> detailTypes; |
1146 | + detailTypes << QContactExtendedDetail::Type; |
1147 | + remoteIdHint.setDetailTypesHint(detailTypes); |
1148 | + |
1149 | + QList<QContact> contacts = iMgr->contacts(localIdList, remoteIdHint); |
1150 | + foreach (const QContact &contact, contacts) { |
1151 | + QString rid = getRemoteId(contact); |
1152 | + aIdList->insertMulti(rid, contact.id()); |
1153 | + } |
1154 | +} |
1155 | + |
1156 | +/*! |
1157 | + \fn GContactsBackend::getContact(QContactId aContactId) |
1158 | + */ |
1159 | +QContact |
1160 | +UContactsBackend::getContact(const QContactId& aContactId) |
1161 | +{ |
1162 | + FUNCTION_CALL_TRACE; |
1163 | + Q_ASSERT (iMgr); |
1164 | + QList<QContact> returnedContacts; |
1165 | + |
1166 | + LOG_DEBUG("Contact ID to be retreived = " << aContactId.toString()); |
1167 | + returnedContacts = iMgr->contacts(QList<QContactId>() << aContactId); |
1168 | + |
1169 | + LOG_DEBUG("Contacts retreived from Contact manager = " << returnedContacts.count()); |
1170 | + return returnedContacts.value(0, QContact()); |
1171 | +} |
1172 | + |
1173 | +QContact |
1174 | +UContactsBackend::getContact(const QString& remoteId) |
1175 | +{ |
1176 | + FUNCTION_CALL_TRACE; |
1177 | + Q_ASSERT (iMgr); |
1178 | + LOG_DEBUG("Remote id to be searched for = " << remoteId); |
1179 | + |
1180 | + // use contact id if possible |
1181 | + QContactId cId = entryExists(remoteId); |
1182 | + if (!cId.isNull()) { |
1183 | + return iMgr->contact(cId); |
1184 | + } |
1185 | + return QContact(); |
1186 | +} |
1187 | + |
1188 | +QContactId |
1189 | +UContactsBackend::entryExists(const QString remoteId) |
1190 | +{ |
1191 | + if (remoteId.isEmpty()) { |
1192 | + return QContactId(); |
1193 | + } |
1194 | + |
1195 | + // check cache |
1196 | + return mRemoteIdToLocalId.value(remoteId); |
1197 | +} |
1198 | + |
1199 | +QString |
1200 | +UContactsBackend::syncTargetId() const |
1201 | +{ |
1202 | + return mSyncTargetId; |
1203 | +} |
1204 | + |
1205 | +const QStringList |
1206 | +UContactsBackend::localIds(const QStringList remoteIds) |
1207 | +{ |
1208 | + QStringList localIdList; |
1209 | + foreach (QString guid , remoteIds) { |
1210 | + QString localId = entryExists(guid).toString(); |
1211 | + if (!localId.isEmpty()) { |
1212 | + localIdList << localId; |
1213 | + } |
1214 | + } |
1215 | + Q_ASSERT(localIdList.count() == remoteIds.count()); |
1216 | + return localIdList; |
1217 | +} |
1218 | + |
1219 | +void UContactsBackend::reloadCache() |
1220 | +{ |
1221 | + FUNCTION_CALL_TRACE; |
1222 | + QContactFetchHint hint; |
1223 | + QList<QContactSortOrder> sortOrder; |
1224 | + QContactFilter sourceFilter; |
1225 | + if (!mSyncTargetId.isEmpty()) { |
1226 | + sourceFilter = getSyncTargetFilter(); |
1227 | + } |
1228 | + |
1229 | + mRemoteIdToLocalId.clear(); |
1230 | + hint.setDetailTypesHint(QList<QContactDetail::DetailType>() << QContactExtendedDetail::Type); |
1231 | + Q_FOREACH(const QContact &c, iMgr->contacts(sourceFilter, sortOrder, hint)) { |
1232 | + QString remoteId = getRemoteId(c); |
1233 | + if (!remoteId.isEmpty()) { |
1234 | + mRemoteIdToLocalId.insert(remoteId, c.id()); |
1235 | + } |
1236 | + } |
1237 | +} |
1238 | + |
1239 | +QString |
1240 | +UContactsBackend::getRemoteId(const QContact &contact) |
1241 | +{ |
1242 | + return UContactsCustomDetail::getCustomField(contact, UContactsCustomDetail::FieldRemoteId).data().toString(); |
1243 | +} |
1244 | + |
1245 | +void UContactsBackend::setRemoteId(QContact &contact, const QString &remoteId) |
1246 | +{ |
1247 | + UContactsCustomDetail::setCustomField(contact, UContactsCustomDetail::FieldRemoteId, QVariant(remoteId)); |
1248 | +} |
1249 | + |
1250 | +QString UContactsBackend::getLocalId(const QContact &contact) |
1251 | +{ |
1252 | + QContactGuid guid = contact.detail<QContactGuid>(); |
1253 | + return guid.guid(); |
1254 | +} |
1255 | + |
1256 | +void UContactsBackend::setLocalId(QContact &contact, const QString &localId) |
1257 | +{ |
1258 | + QContactGuid guid = contact.detail<QContactGuid>(); |
1259 | + guid.setGuid(localId); |
1260 | + contact.saveDetail(&guid); |
1261 | +} |
1262 | + |
1263 | +bool UContactsBackend::deleted(const QContact &contact) |
1264 | +{ |
1265 | + QString deletedAt = UContactsCustomDetail::getCustomField(contact, UContactsCustomDetail::FieldDeletedAt).data().toString(); |
1266 | + return !deletedAt.isEmpty(); |
1267 | +} |
1268 | + |
1269 | +void |
1270 | +UContactsBackend::purgecontacts(const QDateTime &date) |
1271 | +{ |
1272 | + QDBusInterface iface(CPIM_SERVICE_NAME, |
1273 | + CPIM_ADDRESSBOOK_OBJECT_PATH, |
1274 | + CPIM_ADDRESSBOOK_IFACE_NAME); |
1275 | + QDBusReply<void> reply = iface.call("purgeContacts", date.toString(Qt::ISODate), mSyncTargetId); |
1276 | + if (reply.error().isValid()) { |
1277 | + LOG_WARNING("Fail to purge contacts" << reply.error()); |
1278 | + } else { |
1279 | + LOG_DEBUG("Purged backend contacts"); |
1280 | + } |
1281 | +} |
1282 | + |
1283 | +QContactFilter |
1284 | +UContactsBackend::getSyncTargetFilter() const |
1285 | +{ |
1286 | + // user entered contacts, i.e. all other contacts that are not sourcing |
1287 | + // from restricted backends or instant messaging service |
1288 | + static QContactDetailFilter detailFilterDefaultSyncTarget; |
1289 | + |
1290 | + if (!mSyncTargetId.isEmpty() && |
1291 | + detailFilterDefaultSyncTarget.value().isNull()) { |
1292 | + detailFilterDefaultSyncTarget.setDetailType(QContactSyncTarget::Type, |
1293 | + QContactSyncTarget::FieldSyncTarget + 1); |
1294 | + detailFilterDefaultSyncTarget.setValue(mSyncTargetId); |
1295 | + } else if (mSyncTargetId.isEmpty()) { |
1296 | + return QContactFilter(); |
1297 | + } |
1298 | + |
1299 | + return detailFilterDefaultSyncTarget; |
1300 | +} |
1301 | + |
1302 | + |
1303 | +QtContacts::QContactManager *UContactsBackend::manager() const |
1304 | +{ |
1305 | + return iMgr; |
1306 | +} |
1307 | |
1308 | === added file 'buteo-contact-client/UContactsBackend.h' |
1309 | --- buteo-contact-client/UContactsBackend.h 1970-01-01 00:00:00 +0000 |
1310 | +++ buteo-contact-client/UContactsBackend.h 2015-09-24 17:49:24 +0000 |
1311 | @@ -0,0 +1,254 @@ |
1312 | +/* |
1313 | + * This file is part of buteo-sync-plugins-contacts package |
1314 | + * |
1315 | + * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). |
1316 | + * 2015 Canonical Ltd |
1317 | + * |
1318 | + * Contributors: Sateesh Kavuri <sateesh.kavuri@gmail.com> |
1319 | + * Mani Chandrasekar <maninc@gmail.com> |
1320 | + * Renato Araujo Oliveira Filho <renato.filho@canonical.com> |
1321 | + * |
1322 | + * This library is free software; you can redistribute it and/or |
1323 | + * modify it under the terms of the GNU Lesser General Public License |
1324 | + * version 2.1 as published by the Free Software Foundation. |
1325 | + * |
1326 | + * This library is distributed in the hope that it will be useful, but |
1327 | + * WITHOUT ANY WARRANTY; without even the implied warranty of |
1328 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
1329 | + * Lesser General Public License for more details. |
1330 | + * |
1331 | + * You should have received a copy of the GNU Lesser General Public |
1332 | + * License along with this library; if not, write to the Free Software |
1333 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
1334 | + * 02110-1301 USA |
1335 | + * |
1336 | + */ |
1337 | + |
1338 | +#ifndef UCONTACTSBACKEND_H_ |
1339 | +#define UCONTACTSBACKEND_H_ |
1340 | + |
1341 | +#include <QContact> |
1342 | +#include <QContactId> |
1343 | +#include <QContactFetchRequest> |
1344 | +#include <QContactExtendedDetail> |
1345 | +#include <QContactChangeLogFilter> |
1346 | +#include <QContactManager> |
1347 | + |
1348 | +#include <QStringList> |
1349 | + |
1350 | +QTCONTACTS_USE_NAMESPACE |
1351 | + |
1352 | +struct UContactsStatus |
1353 | +{ |
1354 | + int id; |
1355 | + QContactManager::Error errorCode; |
1356 | +}; |
1357 | + |
1358 | +typedef QMultiMap<QString, QContactId> RemoteToLocalIdMap; |
1359 | + |
1360 | +//! \brief Harmattan Contact storage plugin backend interface class |
1361 | +/// |
1362 | +/// This class interfaces with the QtContact backend implementation |
1363 | +class UContactsBackend : public QObject |
1364 | +{ |
1365 | + |
1366 | +public: |
1367 | + explicit UContactsBackend(const QString &managerName = "", QObject* parent = 0); |
1368 | + |
1369 | + /*! |
1370 | + * \brief Destructor |
1371 | + */ |
1372 | + ~UContactsBackend(); |
1373 | + |
1374 | + /*! |
1375 | + * \brief Initialize the backend, must be called before any other function |
1376 | + * \param syncTarget The name of the collection used to store contacts |
1377 | + * \return Returns true if initialized with sucess false otherwise |
1378 | + */ |
1379 | + bool init(uint syncAccount, const QString &syncTarget); |
1380 | + |
1381 | + /*! |
1382 | + * \brief releases the resources held. |
1383 | + * @returnReturns true if sucess false otherwise |
1384 | + */ |
1385 | + bool uninit(); |
1386 | + |
1387 | + /*! |
1388 | + * \brief Return ids of all contacts stored locally |
1389 | + * @return List of contact IDs |
1390 | + */ |
1391 | + QList<QContactId> getAllContactIds(); |
1392 | + |
1393 | + |
1394 | + /*! |
1395 | + * \brief Return all new contacts ids in a QList of QStrings |
1396 | + * @param aTimeStamp Timestamp of the oldest contact ID to be returned |
1397 | + * @return List of contact IDs |
1398 | + */ |
1399 | + RemoteToLocalIdMap getAllNewContactIds(const QDateTime& aTimeStamp); |
1400 | + |
1401 | + /*! |
1402 | + * \brief Return all modified contact ids in a QList of QStrings |
1403 | + * @param aTimeStamp Timestamp of the oldest contact ID to be returned |
1404 | + * @return List of contact IDs |
1405 | + */ |
1406 | + RemoteToLocalIdMap getAllModifiedContactIds(const QDateTime& aTimeStamp); |
1407 | + |
1408 | + |
1409 | + /*! |
1410 | + * \brief Return all deleted contacts ids in a QList of QStrings |
1411 | + * @param aTimeStamp Timestamp of the oldest contact ID to be returned |
1412 | + * @return List of contact IDs |
1413 | + */ |
1414 | + RemoteToLocalIdMap getAllDeletedContactIds(const QDateTime& aTimeStamp); |
1415 | + |
1416 | + /*! |
1417 | + * \brief Get contact data for a given contact ID as a QContact object |
1418 | + * @param aContactId The ID of the contact |
1419 | + * @return The data of the contact |
1420 | + */ |
1421 | + QContact getContact(const QContactId& aContactId); |
1422 | + |
1423 | + /*! |
1424 | + * \brief Returns a contact for the specified remoteId |
1425 | + * @param remoteId The remote id of the contact to be returned |
1426 | + * @return The data of the contact |
1427 | + */ |
1428 | + QContact getContact(const QString& remoteId); |
1429 | + |
1430 | + /*! |
1431 | + * \brief Batch addition of contacts |
1432 | + * @param aContactDataList Contact data |
1433 | + * @param aStatusMap Returned status data |
1434 | + * @return Errors |
1435 | + */ |
1436 | + bool addContacts(QList<QContact>& aContactList, |
1437 | + QMap<int, UContactsStatus> *aStatusMap ); |
1438 | + |
1439 | + // Functions for modifying contacts |
1440 | + /*! |
1441 | + * \brief Batch modification |
1442 | + * @param aContactDataList Contact data |
1443 | + * @param aContactsIdList Contact IDs |
1444 | + * @return Errors |
1445 | + */ |
1446 | + QMap<int, UContactsStatus> modifyContacts(QList<QtContacts::QContact> *aContactList); |
1447 | + |
1448 | + /*! |
1449 | + * \brief Batch deletion of contacts |
1450 | + * @param aContactIDList Contact IDs |
1451 | + * @return Errors |
1452 | + */ |
1453 | + QMap<int, UContactsStatus> deleteContacts(const QStringList &aContactIDList); |
1454 | + |
1455 | + /*! |
1456 | + * \brief Batch deletion of contacts |
1457 | + * @param aContactIDList Contact IDs |
1458 | + * @return Errors |
1459 | + */ |
1460 | + QMap<int, UContactsStatus> deleteContacts(const QList<QContactId> &aContactIDList); |
1461 | + |
1462 | + /*! |
1463 | + * \brief Check if a contact exists |
1464 | + * \param remoteId The remoteId of the contact |
1465 | + * \return The localId of the contact |
1466 | + */ |
1467 | + QContactId entryExists(const QString remoteId); |
1468 | + |
1469 | + /*! |
1470 | + * \brief Retrieve the current address book used by the backend |
1471 | + * \return The address book id |
1472 | + */ |
1473 | + QString syncTargetId() const; |
1474 | + |
1475 | + /*! |
1476 | + * \brief Retrieve the local id of a list of remote ids |
1477 | + * \param remoteIds A list with remote ids |
1478 | + * \return A list with local ids |
1479 | + */ |
1480 | + const QStringList localIds(const QStringList remoteIds); |
1481 | + |
1482 | + /*! |
1483 | + * \brief Return the value of the remote id field in a contact |
1484 | + * \param contact The contact object |
1485 | + * \return A string with the remoteId |
1486 | + */ |
1487 | + static QString getRemoteId(const QContact &contact); |
1488 | + |
1489 | + /*! |
1490 | + * \brief Update the value of the remote id field in a contact |
1491 | + * \param contact The contact object |
1492 | + * \param remoteId A string with the remoteId |
1493 | + */ |
1494 | + static void setRemoteId(QContact &contact, const QString &remoteId); |
1495 | + |
1496 | + /*! |
1497 | + * \brief Return the valueof the local id field in a contact |
1498 | + * \param contact The contact object |
1499 | + * \return A string with the localId |
1500 | + */ |
1501 | + static QString getLocalId(const QContact &contact); |
1502 | + |
1503 | + /*! |
1504 | + * \brief Update the value of the local id field in a contact |
1505 | + * \param contact The contact object |
1506 | + * \param remoteId A string with the localId |
1507 | + */ |
1508 | + static void setLocalId(QContact &contact, const QString &localId); |
1509 | + |
1510 | + /*! |
1511 | + * \brief Check if the contact is marked as deleted |
1512 | + * \param contact The contact object |
1513 | + * \return Returns true if the contact is marked as deleted, otherwise returns false |
1514 | + */ |
1515 | + static bool deleted(const QContact &contact); |
1516 | + |
1517 | + /*! |
1518 | + * \brief Purge all deleted contacts from the server |
1519 | + */ |
1520 | + void purgecontacts(const QDateTime &date); |
1521 | + |
1522 | + /*! |
1523 | + * \brief Reload contact id cache |
1524 | + */ |
1525 | + void reloadCache(); |
1526 | + |
1527 | + |
1528 | + QContactManager *manager() const; |
1529 | + |
1530 | +private: // functions |
1531 | + |
1532 | + /*! |
1533 | + * \brief Returns contact IDs specified by event type and timestamp |
1534 | + * @param aEventType Added/changed/removed contacts |
1535 | + * @param aTimeStamp Contacts older than aTimeStamp are filtered out |
1536 | + * @param aIdList Returned contact IDs |
1537 | + */ |
1538 | + void getSpecifiedContactIds(const QContactChangeLogFilter::EventType aEventType, |
1539 | + const QDateTime &aTimeStamp, |
1540 | + RemoteToLocalIdMap *aIdList); |
1541 | + |
1542 | + /*! |
1543 | + * \brief Constructs and returns the filter for accessing only contacts allowed to be synchronized |
1544 | + * Contacts not allowed to be synchronized are Instant messaging contacts and contacts with origin from other sync backends; |
1545 | + * those contacts have QContactSyncTarget::SyncTarget value different from address book or buteo sync clients. |
1546 | + * It is designed that buteo sync clients don't restrict access to contacts among themselves |
1547 | + * - value for QContactSyncTarget::SyncTarget written by this backend is "buteo". |
1548 | + */ |
1549 | + QContactFilter getSyncTargetFilter() const; |
1550 | + |
1551 | +private: // data |
1552 | + |
1553 | + // if there is more than one Manager we need to have a list of Managers |
1554 | + QContactManager *iMgr; ///< A pointer to contact manager |
1555 | + QString mSyncTargetId; |
1556 | + QMap<QString, QContactId> mRemoteIdToLocalId; |
1557 | + |
1558 | + |
1559 | + void createSourceForAccount(uint accountId, const QString &label); |
1560 | +}; |
1561 | + |
1562 | +#endif /* CONTACTSBACKEND_H_ */ |
1563 | + |
1564 | + |
1565 | + |
1566 | |
1567 | === added file 'buteo-contact-client/UContactsClient.cpp' |
1568 | --- buteo-contact-client/UContactsClient.cpp 1970-01-01 00:00:00 +0000 |
1569 | +++ buteo-contact-client/UContactsClient.cpp 2015-09-24 17:49:24 +0000 |
1570 | @@ -0,0 +1,1028 @@ |
1571 | +/* |
1572 | + * This file is part of buteo-sync-plugins-contacts package |
1573 | + * |
1574 | + * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). |
1575 | + * 2015 Canonical Ltd |
1576 | + * |
1577 | + * Contributors: Sateesh Kavuri <sateesh.kavuri@gmail.com> |
1578 | + * Mani Chandrasekar <maninc@gmail.com> |
1579 | + * Renato Araujo Oliveira Filho <renato.filho@canonical.com> |
1580 | + * |
1581 | + * This library is free software; you can redistribute it and/or |
1582 | + * modify it under the terms of the GNU Lesser General Public License |
1583 | + * version 2.1 as published by the Free Software Foundation. |
1584 | + * |
1585 | + * This library is distributed in the hope that it will be useful, but |
1586 | + * WITHOUT ANY WARRANTY; without even the implied warranty of |
1587 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
1588 | + * Lesser General Public License for more details. |
1589 | + * |
1590 | + * You should have received a copy of the GNU Lesser General Public |
1591 | + * License along with this library; if not, write to the Free Software |
1592 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
1593 | + * 02110-1301 USA |
1594 | + * |
1595 | + */ |
1596 | + |
1597 | +#include "UContactsClient.h" |
1598 | +#include "UContactsBackend.h" |
1599 | +#include "UAbstractRemoteSource.h" |
1600 | +#include "UAuth.h" |
1601 | +#include "config.h" |
1602 | + |
1603 | +//Buteo |
1604 | +#include <LogMacros.h> |
1605 | +#include <ProfileEngineDefs.h> |
1606 | +#include <ProfileManager.h> |
1607 | + |
1608 | +#include <QLibrary> |
1609 | +#include <QtNetwork> |
1610 | +#include <QDateTime> |
1611 | +#include <QContactGuid> |
1612 | +#include <QContactDetailFilter> |
1613 | +#include <QContactAvatar> |
1614 | + |
1615 | +class UContactsClientPrivate |
1616 | +{ |
1617 | +public: |
1618 | + UContactsClientPrivate(const QString &serviceName) |
1619 | + : mAuth(0), |
1620 | + mContactBackend(0), |
1621 | + mRemoteSource(0), |
1622 | + mServiceName(serviceName), |
1623 | + mAborted(false) |
1624 | + { |
1625 | + } |
1626 | + |
1627 | + UAuth* mAuth; |
1628 | + UContactsBackend* mContactBackend; |
1629 | + UAbstractRemoteSource* mRemoteSource; |
1630 | + bool mSlowSync; |
1631 | + bool mAborted; |
1632 | + QString mServiceName; |
1633 | + // local database information |
1634 | + QSet<QContactId> mAllLocalContactIds; |
1635 | + RemoteToLocalIdMap mAddedContactIds; |
1636 | + RemoteToLocalIdMap mModifiedContactIds; |
1637 | + RemoteToLocalIdMap mDeletedContactIds; |
1638 | + // sync report |
1639 | + QMap<QString, Buteo::DatabaseResults> mItemResults; |
1640 | + Buteo::SyncResults mResults; |
1641 | + qreal mProgress; |
1642 | + // sync profile |
1643 | + QString mSyncTarget; |
1644 | + qint32 mAccountId; |
1645 | + Buteo::SyncProfile::SyncDirection mSyncDirection; |
1646 | + Buteo::SyncProfile::ConflictResolutionPolicy mConflictResPolicy; |
1647 | +}; |
1648 | + |
1649 | +UContactsClient::UContactsClient(const QString& aPluginName, |
1650 | + const Buteo::SyncProfile& aProfile, |
1651 | + Buteo::PluginCbInterface *aCbInterface, const QString &serviceName) |
1652 | + : ClientPlugin(aPluginName, aProfile, aCbInterface), |
1653 | + d_ptr(new UContactsClientPrivate(serviceName)) |
1654 | +{ |
1655 | + FUNCTION_CALL_TRACE; |
1656 | +} |
1657 | + |
1658 | +UContactsClient::~UContactsClient() |
1659 | +{ |
1660 | + FUNCTION_CALL_TRACE; |
1661 | + Q_D(UContactsClient); |
1662 | + |
1663 | + delete d->mAuth; |
1664 | + delete d->mRemoteSource; |
1665 | +} |
1666 | + |
1667 | +bool |
1668 | +UContactsClient::init() |
1669 | +{ |
1670 | + FUNCTION_CALL_TRACE; |
1671 | + Q_D(UContactsClient); |
1672 | + |
1673 | + d->mProgress = 0.0; |
1674 | + d->mAborted = false; |
1675 | + |
1676 | + if (lastSyncTime().isNull()) { |
1677 | + d->mSlowSync = true; |
1678 | + } else { |
1679 | + d->mSlowSync = false; |
1680 | + } |
1681 | + |
1682 | + LOG_DEBUG ("Last sync date:" << lastSyncTime() << "Using slow sync?" << d->mSlowSync); |
1683 | + if (!initConfig()) { |
1684 | + LOG_CRITICAL("Fail to init configuration"); |
1685 | + return false; |
1686 | + } |
1687 | + |
1688 | + d->mAuth = crateAuthenticator(this); |
1689 | + if (!d->mAuth || !d->mAuth->init(d->mAccountId, d->mServiceName)) { |
1690 | + LOG_CRITICAL("Fail to create auth object"); |
1691 | + goto init_fail; |
1692 | + } |
1693 | + |
1694 | + d->mContactBackend = createContactsBackend(this); |
1695 | + if (!d->mContactBackend) { |
1696 | + LOG_CRITICAL("Fail to create contact backend"); |
1697 | + goto init_fail; |
1698 | + } |
1699 | + |
1700 | + |
1701 | + // remote source must be initialized after mAuth because its uses the account name property |
1702 | + d->mRemoteSource = createRemoteSource(this); |
1703 | + if (!d->mRemoteSource) { |
1704 | + LOG_CRITICAL("Fail to create remote contact backend"); |
1705 | + goto init_fail; |
1706 | + } |
1707 | + |
1708 | + d->mItemResults.insert(syncTargetId(), Buteo::DatabaseResults()); |
1709 | + |
1710 | + // sign in. |
1711 | + connect(d->mAuth, SIGNAL(success()), SLOT(start())); |
1712 | + connect(d->mAuth, SIGNAL(failed()), SLOT(onAuthenticationError())); |
1713 | + |
1714 | + // syncStateChanged to signal changes from CONNECTING, RECEIVING |
1715 | + // SENDING, DISCONNECTING, CLOSED |
1716 | + connect(this, |
1717 | + SIGNAL(stateChanged(int)), |
1718 | + SLOT(onStateChanged(int))); |
1719 | + |
1720 | + // Take necessary action when sync is finished |
1721 | + connect(this, |
1722 | + SIGNAL(syncFinished(Sync::SyncStatus)), |
1723 | + SLOT(onSyncFinished(Sync::SyncStatus))); |
1724 | + |
1725 | + return true; |
1726 | + |
1727 | +init_fail: |
1728 | + |
1729 | + delete d->mRemoteSource; |
1730 | + delete d->mContactBackend; |
1731 | + delete d->mAuth; |
1732 | + d->mRemoteSource = 0; |
1733 | + d->mContactBackend = 0; |
1734 | + d->mAuth = 0; |
1735 | + return false; |
1736 | +} |
1737 | + |
1738 | +bool |
1739 | +UContactsClient::uninit() |
1740 | +{ |
1741 | + FUNCTION_CALL_TRACE; |
1742 | + Q_D(UContactsClient); |
1743 | + |
1744 | + delete d->mRemoteSource; |
1745 | + delete d->mContactBackend; |
1746 | + delete d->mAuth; |
1747 | + d->mRemoteSource = 0; |
1748 | + d->mContactBackend = 0; |
1749 | + d->mAuth = 0; |
1750 | + |
1751 | + return true; |
1752 | +} |
1753 | + |
1754 | +bool |
1755 | +UContactsClient::isReadyToSync() const |
1756 | +{ |
1757 | + const Q_D(UContactsClient); |
1758 | + return (d->mContactBackend && d->mRemoteSource && d->mAuth); |
1759 | +} |
1760 | + |
1761 | +UContactsBackend *UContactsClient::createContactsBackend(QObject *parent) const |
1762 | +{ |
1763 | + return new UContactsBackend(QCONTACTS_BACKEND_NAME, parent); |
1764 | +} |
1765 | + |
1766 | +UAuth *UContactsClient::crateAuthenticator(QObject *parent) const |
1767 | +{ |
1768 | + return new UAuth(parent); |
1769 | +} |
1770 | + |
1771 | +bool |
1772 | +UContactsClient::startSync() |
1773 | +{ |
1774 | + FUNCTION_CALL_TRACE; |
1775 | + |
1776 | + if (!isReadyToSync()) { |
1777 | + LOG_WARNING ("Ubuntu plugin is not ready to sync."); |
1778 | + return false; |
1779 | + } |
1780 | + |
1781 | + Q_D(UContactsClient); |
1782 | + LOG_DEBUG ("Init done. Continuing with sync"); |
1783 | + |
1784 | + stateChanged(Sync::SYNC_PROGRESS_INITIALISING); |
1785 | + return d->mAuth->authenticate(); |
1786 | +} |
1787 | + |
1788 | +void |
1789 | +UContactsClient::abortSync(Sync::SyncStatus aStatus) |
1790 | +{ |
1791 | + FUNCTION_CALL_TRACE; |
1792 | + Q_D(UContactsClient); |
1793 | + |
1794 | + d->mAborted = true; |
1795 | + d->mRemoteSource->abort(); |
1796 | + emit syncFinished(Sync::SYNC_ABORTED); |
1797 | +} |
1798 | + |
1799 | +bool |
1800 | +UContactsClient::initConfig() |
1801 | +{ |
1802 | + FUNCTION_CALL_TRACE; |
1803 | + Q_D(UContactsClient); |
1804 | + |
1805 | + //TODO: support multiple remote databases "scopes" |
1806 | + QStringList accountList = iProfile.keyValues(Buteo::KEY_ACCOUNT_ID); |
1807 | + if (!accountList.isEmpty()) { |
1808 | + QString aId = accountList.first(); |
1809 | + if (aId != NULL) { |
1810 | + d->mAccountId = aId.toInt(); |
1811 | + } |
1812 | + } else { |
1813 | + d->mAccountId = 0; |
1814 | + LOG_WARNING("Account id not found in config profile"); |
1815 | + return false; |
1816 | + } |
1817 | + |
1818 | + QStringList databaseName = iProfile.keyValues(Buteo::KEY_DISPLAY_NAME); |
1819 | + if (databaseName.isEmpty()) { |
1820 | + LOG_WARNING("\"displayname\" is missing on configuration file"); |
1821 | + return false; |
1822 | + } |
1823 | + d->mSyncTarget = databaseName.first(); |
1824 | + d->mSyncDirection = iProfile.syncDirection(); |
1825 | + d->mConflictResPolicy = iProfile.conflictResolutionPolicy(); |
1826 | + |
1827 | + return true; |
1828 | +} |
1829 | + |
1830 | +void |
1831 | +UContactsClient::onAuthenticationError() |
1832 | +{ |
1833 | + LOG_WARNING("Fail to authenticate with account"); |
1834 | + emit syncFinished (Sync::SYNC_AUTHENTICATION_FAILURE); |
1835 | +} |
1836 | + |
1837 | +bool |
1838 | +UContactsClient::start() |
1839 | +{ |
1840 | + FUNCTION_CALL_TRACE; |
1841 | + Q_D(UContactsClient); |
1842 | + |
1843 | + /* |
1844 | + 1. If no previous sync, go for slow-sync. Fetch all contacts |
1845 | + from server |
1846 | + 2. Check if previous sync happened (from SyncLog). If yes, |
1847 | + fetch the time of last sync |
1848 | + 3. Using the last sync time, retrieve all contacts from server |
1849 | + that were added/modified/deleted |
1850 | + 4. Fetch all added/modified/deleted items from device |
1851 | + 5. Check for conflicts. Take the default policy as "server-wins" |
1852 | + 6. Save the list from the server to device |
1853 | + 7. Push "client changes" - "conflicting items" to the server |
1854 | + 8. Save the sync log |
1855 | + */ |
1856 | + |
1857 | + // Remote source will be create after authentication since it needs some information |
1858 | + // about the authentication (auth-token, etc..) |
1859 | + |
1860 | + LOG_INFO("Sync Started at:" << QDateTime::currentDateTime().toUTC().toString(Qt::ISODate)); |
1861 | + if (d->mAborted) { |
1862 | + LOG_WARNING("Sync aborted"); |
1863 | + return false; |
1864 | + } |
1865 | + |
1866 | + stateChanged(Sync::SYNC_PROGRESS_RECEIVING_ITEMS); |
1867 | + |
1868 | + if (!d->mRemoteSource->init(remoteSourceProperties())) { |
1869 | + LOG_WARNING("Fail to init remote source"); |
1870 | + return false; |
1871 | + } |
1872 | + |
1873 | + if (!d->mContactBackend->init(d->mAccountId, d->mAuth->accountDisplayName())) { |
1874 | + LOG_WARNING("Fail to init contact backend"); |
1875 | + return false; |
1876 | + } |
1877 | + |
1878 | + switch (d->mSyncDirection) |
1879 | + { |
1880 | + case Buteo::SyncProfile::SYNC_DIRECTION_TWO_WAY: |
1881 | + { |
1882 | + QDateTime sinceDate = d->mSlowSync ? QDateTime() : lastSyncTime(); |
1883 | + |
1884 | + LOG_DEBUG("load all contacts since" << sinceDate << sinceDate.isValid()); |
1885 | + // load changed contact since the last sync date or all contacts if no |
1886 | + // sync was done before |
1887 | + loadLocalContacts(sinceDate); |
1888 | + |
1889 | + // load remote contacts |
1890 | + if (d->mSlowSync) { |
1891 | + connect(d->mRemoteSource, |
1892 | + SIGNAL(contactsFetched(QList<QtContacts::QContact>,Sync::SyncStatus,qreal)), |
1893 | + SLOT(onRemoteContactsFetchedForSlowSync(QList<QtContacts::QContact>,Sync::SyncStatus,qreal))); |
1894 | + } else { |
1895 | + connect(d->mRemoteSource, |
1896 | + SIGNAL(contactsFetched(QList<QtContacts::QContact>,Sync::SyncStatus,qreal)), |
1897 | + SLOT(onRemoteContactsFetchedForFastSync(QList<QtContacts::QContact>,Sync::SyncStatus, qreal))); |
1898 | + } |
1899 | + d->mRemoteSource->fetchContacts(sinceDate, !d->mSlowSync, true); |
1900 | + break; |
1901 | + } |
1902 | + case Buteo::SyncProfile::SYNC_DIRECTION_FROM_REMOTE: |
1903 | + LOG_WARNING("SYNC_DIRECTION_FROM_REMOTE: not implemented"); |
1904 | + return false; |
1905 | + case Buteo::SyncProfile::SYNC_DIRECTION_TO_REMOTE: |
1906 | + LOG_WARNING("SYNC_DIRECTION_TO_REMOTE: not implemented"); |
1907 | + return false; |
1908 | + case Buteo::SyncProfile::SYNC_DIRECTION_UNDEFINED: |
1909 | + // Not required |
1910 | + default: |
1911 | + // throw configuration error |
1912 | + return false; |
1913 | + break; |
1914 | + }; |
1915 | + |
1916 | + return true; |
1917 | +} |
1918 | + |
1919 | +QList<QContact> |
1920 | +UContactsClient::prepareContactsToUpload(UContactsBackend *backend, |
1921 | + const QSet<QContactId> &ids) |
1922 | +{ |
1923 | + QList<QContact> toUpdate; |
1924 | + |
1925 | + foreach(const QContactId &id, ids) { |
1926 | + QContact contact = backend->getContact(id); |
1927 | + if (!contact.isEmpty()) { |
1928 | + toUpdate << contact; |
1929 | + } else { |
1930 | + LOG_CRITICAL("Fail to find local contact with id:" << id); |
1931 | + return QList<QContact>(); |
1932 | + } |
1933 | + } |
1934 | + |
1935 | + return toUpdate; |
1936 | +} |
1937 | + |
1938 | +void |
1939 | +UContactsClient::onRemoteContactsFetchedForSlowSync(const QList<QContact> contacts, |
1940 | + Sync::SyncStatus status, |
1941 | + qreal progress) |
1942 | +{ |
1943 | + FUNCTION_CALL_TRACE; |
1944 | + Q_D(UContactsClient); |
1945 | + |
1946 | + if (d->mAborted) { |
1947 | + LOG_WARNING("Sync aborted"); |
1948 | + return; |
1949 | + } |
1950 | + |
1951 | + if ((status != Sync::SYNC_PROGRESS) && (status != Sync::SYNC_STARTED)) { |
1952 | + disconnect(d->mRemoteSource); |
1953 | + } |
1954 | + |
1955 | + if ((status == Sync::SYNC_PROGRESS) || (status == Sync::SYNC_DONE)) { |
1956 | + // save remote contacts locally |
1957 | + storeToLocalForSlowSync(contacts); |
1958 | + |
1959 | + if (status == Sync::SYNC_DONE) { |
1960 | + stateChanged(Sync::SYNC_PROGRESS_SENDING_ITEMS); |
1961 | + QList<QContact> toUpload = prepareContactsToUpload(d->mContactBackend, d->mAllLocalContactIds); |
1962 | + connect(d->mRemoteSource, |
1963 | + SIGNAL(transactionCommited(QList<QtContacts::QContact>, |
1964 | + QList<QtContacts::QContact>, |
1965 | + QStringList, |
1966 | + QMap<QString, int>, |
1967 | + Sync::SyncStatus)), |
1968 | + SLOT(onContactsSavedForSlowSync(QList<QtContacts::QContact>, |
1969 | + QList<QtContacts::QContact>, |
1970 | + QStringList, |
1971 | + QMap<QString, int>, |
1972 | + Sync::SyncStatus))); |
1973 | + |
1974 | + d->mRemoteSource->transaction(); |
1975 | + d->mRemoteSource->saveContacts(toUpload); |
1976 | + d->mRemoteSource->commit(); |
1977 | + } else { |
1978 | + stateChanged(qRound(progress * 100)); |
1979 | + } |
1980 | + } else { |
1981 | + emit syncFinished(status); |
1982 | + } |
1983 | +} |
1984 | + |
1985 | +void |
1986 | +UContactsClient::onContactsSavedForSlowSync(const QList<QtContacts::QContact> &createdContacts, |
1987 | + const QList<QtContacts::QContact> &updatedContacts, |
1988 | + const QStringList &removedContacts, |
1989 | + const QMap<QString, int> errorMap, |
1990 | + Sync::SyncStatus status) |
1991 | +{ |
1992 | + FUNCTION_CALL_TRACE; |
1993 | + Q_D(UContactsClient); |
1994 | + |
1995 | + if (d->mAborted) { |
1996 | + LOG_WARNING("Sync aborted"); |
1997 | + return; |
1998 | + } |
1999 | + |
2000 | + LOG_DEBUG("AFTER UPLOAD(Slow sync):" |
2001 | + << "\n\tCreated on remote:" << createdContacts.size() |
2002 | + << "\n\tUpdated on remote:" << updatedContacts.size() |
2003 | + << "\n\tRemoved from remote:" << removedContacts.size() |
2004 | + << "\n\tError reported:" << errorMap.size()); |
2005 | + |
2006 | + if ((status != Sync::SYNC_PROGRESS) && (status != Sync::SYNC_STARTED)) { |
2007 | + disconnect(d->mRemoteSource); |
2008 | + } |
2009 | + |
2010 | + if ((status == Sync::SYNC_PROGRESS) || (status == Sync::SYNC_DONE)) { |
2011 | + QList<QContact> changedContacts; |
2012 | + |
2013 | + changedContacts += createdContacts; |
2014 | + changedContacts += updatedContacts; |
2015 | + updateIdsToLocal(changedContacts); |
2016 | + handleError(errorMap); |
2017 | + |
2018 | + // sync report |
2019 | + addProcessedItem(Sync::ITEM_ADDED, |
2020 | + Sync::REMOTE_DATABASE, |
2021 | + syncTargetId(), |
2022 | + createdContacts.size()); |
2023 | + |
2024 | + if (status == Sync::SYNC_PROGRESS) { |
2025 | + // sync still in progress |
2026 | + return; |
2027 | + } else { |
2028 | + stateChanged(Sync::SYNC_PROGRESS_FINALISING); |
2029 | + // WORKARDOUND: 'galera' contacts service take a while to fire contacts |
2030 | + // changed singal, this can cause a new sync due the storage change plugin |
2031 | + // lets wait 2 secs before fire sync finished signal |
2032 | + QTimer::singleShot(2000, this, SLOT(fireSyncFinishedSucessfully())); |
2033 | + return; |
2034 | + } |
2035 | + } |
2036 | + |
2037 | + emit syncFinished(status); |
2038 | +} |
2039 | + |
2040 | +void UContactsClient::onRemoteContactsFetchedForFastSync(const QList<QContact> contacts, |
2041 | + Sync::SyncStatus status, |
2042 | + qreal progress) |
2043 | +{ |
2044 | + FUNCTION_CALL_TRACE; |
2045 | + Q_D(UContactsClient); |
2046 | + |
2047 | + if (d->mAborted) { |
2048 | + LOG_WARNING("Sync aborted"); |
2049 | + return; |
2050 | + } |
2051 | + |
2052 | + if ((status != Sync::SYNC_PROGRESS) && (status != Sync::SYNC_STARTED)) { |
2053 | + disconnect(d->mRemoteSource); |
2054 | + } |
2055 | + |
2056 | + if ((status == Sync::SYNC_PROGRESS) || (status == Sync::SYNC_DONE)) { |
2057 | + // save remote contacts locally |
2058 | + storeToLocalForFastSync(contacts); |
2059 | + |
2060 | + if (status == Sync::SYNC_DONE) { |
2061 | + stateChanged(Sync::SYNC_PROGRESS_SENDING_ITEMS); |
2062 | + QList<QContact> contactsToUpload; |
2063 | + QList<QContact> contactsToRemove; |
2064 | + |
2065 | + // Contacts created locally |
2066 | + LOG_DEBUG("Total number of Contacts ADDED : " << d->mAddedContactIds.count()); |
2067 | + contactsToUpload = prepareContactsToUpload(d->mContactBackend, |
2068 | + d->mAddedContactIds.values().toSet()); |
2069 | + |
2070 | + // Contacts modified locally |
2071 | + LOG_DEBUG("Total number of Contacts MODIFIED : " << d->mModifiedContactIds.count()); |
2072 | + contactsToUpload += prepareContactsToUpload(d->mContactBackend, |
2073 | + d->mModifiedContactIds.values().toSet()); |
2074 | + |
2075 | + // Contacts deleted locally |
2076 | + LOG_DEBUG("Total number of Contacts DELETED : " << d->mDeletedContactIds.count()); |
2077 | + contactsToRemove = prepareContactsToUpload(d->mContactBackend, |
2078 | + d->mDeletedContactIds.values().toSet()); |
2079 | + |
2080 | + connect(d->mRemoteSource, |
2081 | + SIGNAL(transactionCommited(QList<QtContacts::QContact>, |
2082 | + QList<QtContacts::QContact>, |
2083 | + QStringList, |
2084 | + QMap<QString, int>, |
2085 | + Sync::SyncStatus)), |
2086 | + SLOT(onContactsSavedForFastSync(QList<QtContacts::QContact>, |
2087 | + QList<QtContacts::QContact>, |
2088 | + QStringList, |
2089 | + QMap<QString, int>, |
2090 | + Sync::SyncStatus))); |
2091 | + |
2092 | + d->mRemoteSource->transaction(); |
2093 | + d->mRemoteSource->saveContacts(contactsToUpload); |
2094 | + d->mRemoteSource->removeContacts(contactsToRemove); |
2095 | + d->mRemoteSource->commit(); |
2096 | + } else { |
2097 | + stateChanged(qRound(progress*100)); |
2098 | + } |
2099 | + } else { |
2100 | + emit syncFinished(status); |
2101 | + } |
2102 | +} |
2103 | + |
2104 | +void |
2105 | +UContactsClient::onContactsSavedForFastSync(const QList<QtContacts::QContact> &createdContacts, |
2106 | + const QList<QtContacts::QContact> &updatedContacts, |
2107 | + const QStringList &removedContacts, |
2108 | + const QMap<QString, int> errorMap, |
2109 | + Sync::SyncStatus status) |
2110 | +{ |
2111 | + Q_UNUSED(updatedContacts) |
2112 | + Q_UNUSED(removedContacts) |
2113 | + FUNCTION_CALL_TRACE; |
2114 | + Q_D(UContactsClient); |
2115 | + |
2116 | + if (d->mAborted) { |
2117 | + LOG_WARNING("Sync aborted"); |
2118 | + return; |
2119 | + } |
2120 | + |
2121 | + LOG_DEBUG("AFTER UPLOAD(Fast sync):" << status |
2122 | + << "\n\tCreated on remote:" << createdContacts.size() |
2123 | + << "\n\tUpdated on remote:" << updatedContacts.size() |
2124 | + << "\n\tRemoved from remote:" << removedContacts.size() |
2125 | + << "\n\tError reported:" << errorMap.size()); |
2126 | + |
2127 | + if ((status != Sync::SYNC_PROGRESS) && (status != Sync::SYNC_STARTED)) { |
2128 | + disconnect(d->mRemoteSource); |
2129 | + } |
2130 | + |
2131 | + if ((status == Sync::SYNC_PROGRESS) || (status == Sync::SYNC_DONE)) { |
2132 | + QList<QContact> changedContacts; |
2133 | + |
2134 | + changedContacts += createdContacts; |
2135 | + changedContacts += updatedContacts; |
2136 | + |
2137 | + updateIdsToLocal(changedContacts); |
2138 | + handleError(errorMap); |
2139 | + |
2140 | + // we need to call delete again because of error handling |
2141 | + d->mContactBackend->deleteContacts(removedContacts); |
2142 | + |
2143 | + // sync report |
2144 | + addProcessedItem(Sync::ITEM_ADDED, |
2145 | + Sync::REMOTE_DATABASE, |
2146 | + syncTargetId(), |
2147 | + createdContacts.size()); |
2148 | + addProcessedItem(Sync::ITEM_MODIFIED, |
2149 | + Sync::REMOTE_DATABASE, |
2150 | + syncTargetId(), |
2151 | + updatedContacts.size()); |
2152 | + addProcessedItem(Sync::ITEM_DELETED, |
2153 | + Sync::REMOTE_DATABASE, |
2154 | + syncTargetId(), |
2155 | + removedContacts.size()); |
2156 | + |
2157 | + if (status == Sync::SYNC_PROGRESS) { |
2158 | + // sync still in progress |
2159 | + return; |
2160 | + } else { |
2161 | + stateChanged(Sync::SYNC_PROGRESS_FINALISING); |
2162 | + // WORKARDOUND: 'galera' contacts service take a while to fire contacts |
2163 | + // changed singal, this can cause a new sync due the storage change plugin |
2164 | + // lets wait 2 secs before fire sync finished signal |
2165 | + QTimer::singleShot(2000, this, SLOT(fireSyncFinishedSucessfully())); |
2166 | + return; |
2167 | + } |
2168 | + } |
2169 | + |
2170 | + emit syncFinished(status); |
2171 | +} |
2172 | + |
2173 | +void |
2174 | +UContactsClient::fireSyncFinishedSucessfully() |
2175 | +{ |
2176 | + emit syncFinished(Sync::SYNC_DONE); |
2177 | +} |
2178 | + |
2179 | +void UContactsClient::handleError(const QMap<QString, int> &errorMap) |
2180 | +{ |
2181 | + Q_D(UContactsClient); |
2182 | + QStringList contactToRemove; |
2183 | + |
2184 | + QMap<QString, int>::const_iterator i = errorMap.begin(); |
2185 | + while(i != errorMap.end()) { |
2186 | + switch(i.value()) { |
2187 | + case QContactManager::DoesNotExistError: |
2188 | + // if the contact does not exists on remote side we will remove it locally |
2189 | + LOG_DEBUG("Romoving contact locally due the remote error:" << i.key()); |
2190 | + contactToRemove << i.key(); |
2191 | + break; |
2192 | + default: |
2193 | + LOG_WARNING("Unexpected error:" << i.value()); |
2194 | + break; |
2195 | + } |
2196 | + i++; |
2197 | + } |
2198 | + |
2199 | + if (!contactToRemove.isEmpty()) { |
2200 | + d->mContactBackend->deleteContacts(contactToRemove); |
2201 | + } |
2202 | +} |
2203 | + |
2204 | +bool |
2205 | +UContactsClient::storeToLocalForSlowSync(const QList<QContact> &remoteContacts) |
2206 | +{ |
2207 | + FUNCTION_CALL_TRACE; |
2208 | + |
2209 | + Q_D(UContactsClient); |
2210 | + Q_ASSERT(d->mSlowSync); |
2211 | + |
2212 | + bool syncSuccess = false; |
2213 | + |
2214 | + LOG_DEBUG ("@@@storeToLocal#SLOW SYNC"); |
2215 | + // Since we request for all the deleted contacts, if |
2216 | + // slow sync is performed many times, even deleted contacts |
2217 | + // will appear in *remoteContacts. Filter them out while |
2218 | + // saving them to device |
2219 | + LOG_DEBUG ("TOTAL REMOTE CONTACTS:" << remoteContacts.size()); |
2220 | + |
2221 | + if (!remoteContacts.isEmpty()) { |
2222 | + QMap<int, UContactsStatus> statusMap; |
2223 | + QList<QContact> cpyContacts(remoteContacts); |
2224 | + if (d->mContactBackend->addContacts(cpyContacts, &statusMap)) { |
2225 | + // TODO: Saving succeeded. Update sync results |
2226 | + syncSuccess = true; |
2227 | + |
2228 | + // sync report |
2229 | + addProcessedItem(Sync::ITEM_ADDED, |
2230 | + Sync::LOCAL_DATABASE, |
2231 | + syncTargetId(), |
2232 | + cpyContacts.count()); |
2233 | + } else { |
2234 | + // TODO: Saving failed. Update sync results and probably stop sync |
2235 | + syncSuccess = false; |
2236 | + } |
2237 | + } |
2238 | + |
2239 | + return syncSuccess; |
2240 | +} |
2241 | + |
2242 | +bool |
2243 | +UContactsClient::storeToLocalForFastSync(const QList<QContact> &remoteContacts) |
2244 | +{ |
2245 | + FUNCTION_CALL_TRACE; |
2246 | + Q_D(UContactsClient); |
2247 | + Q_ASSERT(!d->mSlowSync); |
2248 | + |
2249 | + bool syncSuccess = false; |
2250 | + LOG_DEBUG ("@@@storeToLocal#FAST SYNC"); |
2251 | + QList<QContact> remoteAddedContacts, remoteModifiedContacts, remoteDeletedContacts; |
2252 | + filterRemoteAddedModifiedDeletedContacts(remoteContacts, |
2253 | + remoteAddedContacts, |
2254 | + remoteModifiedContacts, |
2255 | + remoteDeletedContacts); |
2256 | + |
2257 | + resolveConflicts(remoteModifiedContacts, remoteDeletedContacts); |
2258 | + |
2259 | + if (!remoteAddedContacts.isEmpty()) { |
2260 | + LOG_DEBUG ("***Adding " << remoteAddedContacts.size() << " contacts"); |
2261 | + QMap<int, UContactsStatus> addedStatusMap; |
2262 | + syncSuccess = d->mContactBackend->addContacts(remoteAddedContacts, &addedStatusMap); |
2263 | + |
2264 | + if (syncSuccess) { |
2265 | + // sync report |
2266 | + addProcessedItem(Sync::ITEM_ADDED, |
2267 | + Sync::LOCAL_DATABASE, |
2268 | + syncTargetId(), |
2269 | + remoteAddedContacts.count()); |
2270 | + } |
2271 | + } |
2272 | + |
2273 | + if (!remoteModifiedContacts.isEmpty()) { |
2274 | + LOG_DEBUG ("***Modifying " << remoteModifiedContacts.size() << " contacts"); |
2275 | + QMap<int, UContactsStatus> modifiedStatusMap = |
2276 | + d->mContactBackend->modifyContacts(&remoteModifiedContacts); |
2277 | + |
2278 | + syncSuccess = (modifiedStatusMap.size() > 0); |
2279 | + |
2280 | + if (syncSuccess) { |
2281 | + // sync report |
2282 | + addProcessedItem(Sync::ITEM_MODIFIED, |
2283 | + Sync::LOCAL_DATABASE, |
2284 | + syncTargetId(), |
2285 | + modifiedStatusMap.size()); |
2286 | + } |
2287 | + } |
2288 | + |
2289 | + if (!remoteDeletedContacts.isEmpty()) { |
2290 | + LOG_DEBUG ("***Deleting " << remoteDeletedContacts.size() << " contacts"); |
2291 | + QStringList guidList; |
2292 | + for (int i=0; i<remoteDeletedContacts.size(); i++) { |
2293 | + guidList << UContactsBackend::getRemoteId(remoteDeletedContacts.at(i)); |
2294 | + } |
2295 | + |
2296 | + QStringList localIdList = d->mContactBackend->localIds(guidList); |
2297 | + QMap<int, UContactsStatus> deletedStatusMap = |
2298 | + d->mContactBackend->deleteContacts(localIdList); |
2299 | + |
2300 | + syncSuccess = (deletedStatusMap.size() > 0); |
2301 | + if (syncSuccess) { |
2302 | + // sync report |
2303 | + addProcessedItem(Sync::ITEM_DELETED, |
2304 | + Sync::LOCAL_DATABASE, |
2305 | + syncTargetId(), |
2306 | + localIdList.size()); |
2307 | + } |
2308 | + } |
2309 | + |
2310 | + return syncSuccess; |
2311 | +} |
2312 | + |
2313 | +bool |
2314 | +UContactsClient::cleanUp() |
2315 | +{ |
2316 | + FUNCTION_CALL_TRACE; |
2317 | + //TODO |
2318 | + return true; |
2319 | +} |
2320 | + |
2321 | +void UContactsClient::connectivityStateChanged(Sync::ConnectivityType aType, bool aState) |
2322 | +{ |
2323 | + FUNCTION_CALL_TRACE; |
2324 | + LOG_DEBUG("Received connectivity change event:" << aType << " changed to " << aState); |
2325 | +} |
2326 | + |
2327 | +void |
2328 | +UContactsClient::loadLocalContacts(const QDateTime &since) |
2329 | +{ |
2330 | + FUNCTION_CALL_TRACE; |
2331 | + Q_D(UContactsClient); |
2332 | + |
2333 | + if (!since.isValid()) { |
2334 | + d->mAllLocalContactIds = d->mContactBackend->getAllContactIds().toSet(); |
2335 | + |
2336 | + LOG_DEBUG ("Number of contacts:" << d->mAllLocalContactIds.size ()); |
2337 | + } else { |
2338 | + d->mAddedContactIds = d->mContactBackend->getAllNewContactIds(since); |
2339 | + d->mModifiedContactIds = d->mContactBackend->getAllModifiedContactIds(since); |
2340 | + d->mDeletedContactIds = d->mContactBackend->getAllDeletedContactIds(since); |
2341 | + |
2342 | + LOG_DEBUG ("Number of local added contacts:" << d->mAddedContactIds.size()); |
2343 | + LOG_DEBUG ("Number of local modified contacts:" << d->mModifiedContactIds.size()); |
2344 | + LOG_DEBUG ("Number of local removed contacts:" << d->mDeletedContactIds.size()); |
2345 | + } |
2346 | +} |
2347 | + |
2348 | +void |
2349 | +UContactsClient::onStateChanged(int aState) |
2350 | +{ |
2351 | + FUNCTION_CALL_TRACE; |
2352 | + emit syncProgressDetail(getProfileName(), aState); |
2353 | +} |
2354 | + |
2355 | +void |
2356 | +UContactsClient::onSyncFinished(Sync::SyncStatus aState) |
2357 | +{ |
2358 | + FUNCTION_CALL_TRACE; |
2359 | + Q_D(UContactsClient); |
2360 | + |
2361 | + switch(aState) |
2362 | + { |
2363 | + case Sync::SYNC_ERROR: |
2364 | + case Sync::SYNC_AUTHENTICATION_FAILURE: |
2365 | + case Sync::SYNC_DATABASE_FAILURE: |
2366 | + case Sync::SYNC_CONNECTION_ERROR: |
2367 | + case Sync::SYNC_NOTPOSSIBLE: |
2368 | + { |
2369 | + generateResults(false); |
2370 | + emit error(getProfileName(), "", aState); |
2371 | + break; |
2372 | + } |
2373 | + case Sync::SYNC_DONE: |
2374 | + // purge all deleted contacts |
2375 | + d->mContactBackend->purgecontacts(lastSyncTime()); |
2376 | + case Sync::SYNC_ABORTED: |
2377 | + { |
2378 | + generateResults(true); |
2379 | + emit success(getProfileName(), QString::number(aState)); |
2380 | + break; |
2381 | + } |
2382 | + case Sync::SYNC_QUEUED: |
2383 | + case Sync::SYNC_STARTED: |
2384 | + case Sync::SYNC_PROGRESS: |
2385 | + default: |
2386 | + { |
2387 | + generateResults(false); |
2388 | + emit error(getProfileName(), "", aState); |
2389 | + break; |
2390 | + } |
2391 | + } |
2392 | +} |
2393 | + |
2394 | +Buteo::SyncResults |
2395 | +UContactsClient::getSyncResults() const |
2396 | +{ |
2397 | + return d_ptr->mResults; |
2398 | +} |
2399 | + |
2400 | +QString |
2401 | +UContactsClient::authToken() const |
2402 | +{ |
2403 | + return d_ptr->mAuth->token(); |
2404 | +} |
2405 | + |
2406 | +QString |
2407 | +UContactsClient::syncTargetId() const |
2408 | +{ |
2409 | + return d_ptr->mContactBackend->syncTargetId(); |
2410 | +} |
2411 | + |
2412 | +QString UContactsClient::accountName() const |
2413 | +{ |
2414 | + if (d_ptr->mAuth) { |
2415 | + return d_ptr->mAuth->accountDisplayName(); |
2416 | + } |
2417 | + return QString(); |
2418 | +} |
2419 | + |
2420 | +const QDateTime |
2421 | +UContactsClient::lastSyncTime() const |
2422 | +{ |
2423 | + FUNCTION_CALL_TRACE; |
2424 | + |
2425 | + Buteo::ProfileManager pm; |
2426 | + Buteo::SyncProfile* sp = pm.syncProfile (iProfile.name ()); |
2427 | + QDateTime lastTime = sp->lastSuccessfulSyncTime(); |
2428 | + if (!lastTime.isNull()) { |
2429 | + // return UTC time used by google |
2430 | + return lastTime.addSecs(3).toUTC(); |
2431 | + } else { |
2432 | + return lastTime; |
2433 | + } |
2434 | +} |
2435 | + |
2436 | + |
2437 | +void |
2438 | +UContactsClient::filterRemoteAddedModifiedDeletedContacts(const QList<QContact> remoteContacts, |
2439 | + QList<QContact> &remoteAddedContacts, |
2440 | + QList<QContact> &remoteModifiedContacts, |
2441 | + QList<QContact> &remoteDeletedContacts) |
2442 | +{ |
2443 | + FUNCTION_CALL_TRACE; |
2444 | + Q_D(UContactsClient); |
2445 | + |
2446 | + foreach (const QContact &contact, remoteContacts) { |
2447 | + if (UContactsBackend::deleted(contact)) { |
2448 | + remoteDeletedContacts.append(contact); |
2449 | + continue; |
2450 | + } |
2451 | + |
2452 | + QString remoteId = UContactsBackend::getRemoteId(contact); |
2453 | + QContactId localId = d->mContactBackend->entryExists(remoteId); |
2454 | + if (localId.isNull()) { |
2455 | + remoteAddedContacts.append(contact); |
2456 | + } else { |
2457 | + remoteModifiedContacts.append(contact); |
2458 | + } |
2459 | + } |
2460 | +} |
2461 | + |
2462 | +void |
2463 | +UContactsClient::resolveConflicts(QList<QContact> &modifiedRemoteContacts, |
2464 | + QList<QContact> &deletedRemoteContacts) |
2465 | +{ |
2466 | + FUNCTION_CALL_TRACE; |
2467 | + Q_D(UContactsClient); |
2468 | + |
2469 | + // TODO: Handle conflicts. The steps: |
2470 | + // o Compare the list of local modified/deleted contacts with |
2471 | + // the list of remote modified/deleted contacts |
2472 | + // o Create a new list (a map maybe) that has the contacts to |
2473 | + // be modified/deleted using the conflict resolution policy |
2474 | + // (server-wins, client-wins, add-new) |
2475 | + // o Return the list |
2476 | + |
2477 | + //QListIterator<GContactEntry*> iter (modifiedRemoteContacts); |
2478 | + QList<QContact>::iterator iter; |
2479 | + for (iter = modifiedRemoteContacts.begin (); iter != modifiedRemoteContacts.end (); ++iter) { |
2480 | + QContact contact = *iter; |
2481 | + QString remoteId = UContactsBackend::getRemoteId(contact); |
2482 | + |
2483 | + if (d->mModifiedContactIds.contains(remoteId)) { |
2484 | + if (d->mConflictResPolicy == Buteo::SyncProfile::CR_POLICY_PREFER_LOCAL_CHANGES) { |
2485 | + modifiedRemoteContacts.erase(iter); |
2486 | + } else { |
2487 | + d->mModifiedContactIds.remove(remoteId); |
2488 | + } |
2489 | + } |
2490 | + |
2491 | + if (d->mDeletedContactIds.contains(remoteId)) { |
2492 | + if (d->mConflictResPolicy == Buteo::SyncProfile::CR_POLICY_PREFER_LOCAL_CHANGES) { |
2493 | + modifiedRemoteContacts.erase(iter); |
2494 | + } else { |
2495 | + d->mDeletedContactIds.remove(remoteId); |
2496 | + } |
2497 | + } |
2498 | + } |
2499 | + |
2500 | + for (iter = deletedRemoteContacts.begin (); iter != deletedRemoteContacts.end (); ++iter) { |
2501 | + QContact contact = *iter; |
2502 | + QString remoteId = UContactsBackend::getRemoteId(contact); |
2503 | + |
2504 | + if (d->mModifiedContactIds.contains(remoteId)) { |
2505 | + if (d->mConflictResPolicy == Buteo::SyncProfile::CR_POLICY_PREFER_LOCAL_CHANGES) { |
2506 | + deletedRemoteContacts.erase(iter); |
2507 | + } else { |
2508 | + d->mModifiedContactIds.remove(remoteId); |
2509 | + } |
2510 | + } |
2511 | + |
2512 | + if (d->mDeletedContactIds.contains(remoteId)) { |
2513 | + // If the entry is deleted both at the server and |
2514 | + // locally, then just remove it from the lists |
2515 | + // so that no further action need to be taken |
2516 | + deletedRemoteContacts.erase(iter); |
2517 | + d->mDeletedContactIds.remove(remoteId); |
2518 | + } |
2519 | + } |
2520 | +} |
2521 | + |
2522 | +void |
2523 | +UContactsClient::updateIdsToLocal(const QList<QContact> &contacts) |
2524 | +{ |
2525 | + FUNCTION_CALL_TRACE; |
2526 | + Q_D(UContactsClient); |
2527 | + QList<QContact> newList(contacts); |
2528 | + d->mContactBackend->modifyContacts(&newList); |
2529 | +} |
2530 | + |
2531 | +void |
2532 | +UContactsClient::addProcessedItem(Sync::TransferType modificationType, |
2533 | + Sync::TransferDatabase database, |
2534 | + const QString &modifiedDatabase, |
2535 | + int count) |
2536 | +{ |
2537 | + FUNCTION_CALL_TRACE; |
2538 | + Q_D(UContactsClient); |
2539 | + |
2540 | + Buteo::DatabaseResults& results = d->mItemResults[modifiedDatabase]; |
2541 | + if (database == Sync::LOCAL_DATABASE) { |
2542 | + if (modificationType == Sync::ITEM_ADDED) { |
2543 | + results.iLocalItemsAdded += count; |
2544 | + } else if (modificationType == Sync::ITEM_MODIFIED) { |
2545 | + results.iLocalItemsModified += count; |
2546 | + } else if (modificationType == Sync::ITEM_DELETED) { |
2547 | + results.iLocalItemsDeleted += count; |
2548 | + } |
2549 | + } else if (database == Sync::REMOTE_DATABASE) { |
2550 | + if( modificationType == Sync::ITEM_ADDED) { |
2551 | + results.iRemoteItemsAdded += count; |
2552 | + } else if (modificationType == Sync::ITEM_MODIFIED) { |
2553 | + results.iRemoteItemsModified += count; |
2554 | + } else if (modificationType == Sync::ITEM_DELETED) { |
2555 | + results.iRemoteItemsDeleted += count; |
2556 | + } |
2557 | + } |
2558 | + |
2559 | + emit transferProgress(getProfileName(), database, modificationType, "text/vcard", count); |
2560 | +} |
2561 | + |
2562 | +void |
2563 | +UContactsClient::generateResults(bool aSuccessful) |
2564 | +{ |
2565 | + FUNCTION_CALL_TRACE; |
2566 | + Q_D(UContactsClient); |
2567 | + |
2568 | + d->mResults = Buteo::SyncResults(); |
2569 | + d->mResults.setMajorCode(aSuccessful ? Buteo::SyncResults::SYNC_RESULT_SUCCESS : |
2570 | + Buteo::SyncResults::SYNC_RESULT_FAILED ); |
2571 | + d->mResults.setTargetId(iProfile.name()); |
2572 | + if (d->mItemResults.isEmpty()) { |
2573 | + LOG_INFO("No items transferred"); |
2574 | + } else { |
2575 | + QMapIterator<QString, Buteo::DatabaseResults> i(d->mItemResults); |
2576 | + while (i.hasNext()) |
2577 | + { |
2578 | + i.next(); |
2579 | + const Buteo::DatabaseResults &r = i.value(); |
2580 | + Buteo::TargetResults targetResults(i.key(), // Target name |
2581 | + Buteo::ItemCounts(r.iLocalItemsAdded, |
2582 | + r.iLocalItemsDeleted, |
2583 | + r.iLocalItemsModified), |
2584 | + Buteo::ItemCounts(r.iRemoteItemsAdded, |
2585 | + r.iRemoteItemsDeleted, |
2586 | + r.iRemoteItemsModified)); |
2587 | + d->mResults.addTargetResults(targetResults); |
2588 | + LOG_INFO("Sync finished at:" << d->mResults.syncTime().toUTC().toString(Qt::ISODate)); |
2589 | + LOG_INFO("Items for" << targetResults.targetName() << ":"); |
2590 | + LOG_INFO("LA:" << targetResults.localItems().added << |
2591 | + "LD:" << targetResults.localItems().deleted << |
2592 | + "LM:" << targetResults.localItems().modified << |
2593 | + "RA:" << targetResults.remoteItems().added << |
2594 | + "RD:" << targetResults.remoteItems().deleted << |
2595 | + "RM:" << targetResults.remoteItems().modified); |
2596 | + } |
2597 | + } |
2598 | +} |
2599 | |
2600 | === added file 'buteo-contact-client/UContactsClient.h' |
2601 | --- buteo-contact-client/UContactsClient.h 1970-01-01 00:00:00 +0000 |
2602 | +++ buteo-contact-client/UContactsClient.h 2015-09-24 17:49:24 +0000 |
2603 | @@ -0,0 +1,170 @@ |
2604 | +/* |
2605 | + * This file is part of buteo-sync-plugins-contacts package |
2606 | + * |
2607 | + * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). |
2608 | + * 2015 Canonical Ltd |
2609 | + * |
2610 | + * Contributors: Sateesh Kavuri <sateesh.kavuri@gmail.com> |
2611 | + * Mani Chandrasekar <maninc@gmail.com> |
2612 | + * Renato Araujo Oliveira Filho <renato.filho@canonical.com> |
2613 | + * |
2614 | + * This library is free software; you can redistribute it and/or |
2615 | + * modify it under the terms of the GNU Lesser General Public License |
2616 | + * version 2.1 as published by the Free Software Foundation. |
2617 | + * |
2618 | + * This library is distributed in the hope that it will be useful, but |
2619 | + * WITHOUT ANY WARRANTY; without even the implied warranty of |
2620 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
2621 | + * Lesser General Public License for more details. |
2622 | + * |
2623 | + * You should have received a copy of the GNU Lesser General Public |
2624 | + * License along with this library; if not, write to the Free Software |
2625 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
2626 | + * 02110-1301 USA |
2627 | + * |
2628 | + */ |
2629 | + |
2630 | +#ifndef UCONTACSTCLIENT_H |
2631 | +#define UCONTACSTCLIENT_H |
2632 | + |
2633 | +#include <QNetworkReply> |
2634 | +#include <QContact> |
2635 | +#include <QList> |
2636 | +#include <QPair> |
2637 | + |
2638 | +#include <ClientPlugin.h> |
2639 | + |
2640 | +class UContactsClientPrivate; |
2641 | +class UAuth; |
2642 | +class UAbstractRemoteSource; |
2643 | +class UContactsBackend; |
2644 | + |
2645 | +class UContactsClient : public Buteo::ClientPlugin |
2646 | +{ |
2647 | + Q_OBJECT |
2648 | + Q_DECLARE_PRIVATE(UContactsClient) |
2649 | + |
2650 | +public: |
2651 | + |
2652 | + /*! \brief Constructor |
2653 | + * |
2654 | + * @param aPluginName Name of this client plugin |
2655 | + * @param aProfile Sync profile |
2656 | + * @param aCbInterface Pointer to the callback interface |
2657 | + * @param authenticator a instance of UAuth class to be used during the authentication |
2658 | + */ |
2659 | + UContactsClient(const QString& aPluginName, |
2660 | + const Buteo::SyncProfile &aProfile, |
2661 | + Buteo::PluginCbInterface *aCbInterface, |
2662 | + const QString& serviceName); |
2663 | + |
2664 | + /*! \brief Destructor |
2665 | + * |
2666 | + * Call uninit before destroying the object. |
2667 | + */ |
2668 | + virtual ~UContactsClient(); |
2669 | + |
2670 | + //! @see SyncPluginBase::init |
2671 | + virtual bool init(); |
2672 | + |
2673 | + //! @see SyncPluginBase::uninit |
2674 | + virtual bool uninit(); |
2675 | + |
2676 | + //! @see ClientPlugin::startSync |
2677 | + virtual bool startSync(); |
2678 | + |
2679 | + //! @see SyncPluginBase::abortSync |
2680 | + virtual void abortSync(Sync::SyncStatus aStatus = Sync::SYNC_ABORTED); |
2681 | + |
2682 | + //! @see SyncPluginBase::getSyncResults |
2683 | + virtual Buteo::SyncResults getSyncResults() const; |
2684 | + |
2685 | + //! @see SyncPluginBase::cleanUp |
2686 | + virtual bool cleanUp(); |
2687 | + |
2688 | +public slots: |
2689 | + //! @see SyncPluginBase::connectivityStateChanged |
2690 | + virtual void connectivityStateChanged( Sync::ConnectivityType aType, |
2691 | + bool aState ); |
2692 | + |
2693 | +protected: |
2694 | + QString authToken() const; |
2695 | + QString syncTargetId() const; |
2696 | + QString accountName() const; |
2697 | + |
2698 | + // Must be implemented by the plugins |
2699 | + virtual UAbstractRemoteSource* createRemoteSource(QObject *parent) const = 0; |
2700 | + virtual QVariantMap remoteSourceProperties() const = 0; |
2701 | + |
2702 | + virtual UContactsBackend* createContactsBackend(QObject *parent) const; |
2703 | + virtual UAuth* crateAuthenticator(QObject *parent) const; |
2704 | + |
2705 | + virtual bool isReadyToSync() const; |
2706 | + virtual const QDateTime lastSyncTime() const; |
2707 | + |
2708 | +signals: |
2709 | + void stateChanged(int progress); |
2710 | + void itemProcessed(Sync::TransferType type, |
2711 | + Sync::TransferDatabase db, |
2712 | + int committedItems); |
2713 | + void syncFinished(Sync::SyncStatus); |
2714 | + |
2715 | + |
2716 | +private: |
2717 | + QScopedPointer<UContactsClientPrivate> d_ptr; |
2718 | + |
2719 | + void loadLocalContacts(const QDateTime &since); |
2720 | + |
2721 | + bool initConfig(); |
2722 | + void generateResults(bool aSuccessful); |
2723 | + void updateIdsToLocal(const QList<QtContacts::QContact> &contacts); |
2724 | + void filterRemoteAddedModifiedDeletedContacts(const QList<QTCONTACTS_PREPEND_NAMESPACE(QContact)> remoteContacts, |
2725 | + QList<QTCONTACTS_PREPEND_NAMESPACE(QContact)> &remoteAddedContacts, |
2726 | + QList<QTCONTACTS_PREPEND_NAMESPACE(QContact)> &remoteModifiedContacts, |
2727 | + QList<QTCONTACTS_PREPEND_NAMESPACE(QContact)> &remoteDeletedContacts); |
2728 | + void resolveConflicts(QList<QTCONTACTS_PREPEND_NAMESPACE(QContact)> &modifiedRemoteContacts, |
2729 | + QList<QTCONTACTS_PREPEND_NAMESPACE(QContact)> &deletedRemoteContacts); |
2730 | + void addProcessedItem(Sync::TransferType modificationType, |
2731 | + Sync::TransferDatabase database, |
2732 | + const QString &modifiedDatabase, |
2733 | + int count = 1); |
2734 | + QList<QTCONTACTS_PREPEND_NAMESPACE(QContact)> prepareContactsToUpload(UContactsBackend *backend, |
2735 | + const QSet<QTCONTACTS_PREPEND_NAMESPACE(QContactId)> &ids); |
2736 | + |
2737 | + /* slow sync */ |
2738 | + bool storeToLocalForSlowSync(const QList<QTCONTACTS_PREPEND_NAMESPACE(QContact)> &remoteContacts); |
2739 | + |
2740 | + /* fast sync */ |
2741 | + bool storeToLocalForFastSync(const QList<QTCONTACTS_PREPEND_NAMESPACE(QContact)> &remoteContacts); |
2742 | + |
2743 | +private slots: |
2744 | + bool start(); |
2745 | + void onAuthenticationError(); |
2746 | + void onStateChanged(int progress); |
2747 | + void onSyncFinished(Sync::SyncStatus status); |
2748 | + void fireSyncFinishedSucessfully(); |
2749 | + void handleError(const QMap<QString, int> &errorMap); |
2750 | + |
2751 | + |
2752 | + /* slow sync */ |
2753 | + void onRemoteContactsFetchedForSlowSync(const QList<QtContacts::QContact> contacts, |
2754 | + Sync::SyncStatus status, |
2755 | + qreal progress); |
2756 | + void onContactsSavedForSlowSync(const QList<QtContacts::QContact> &createdContacts, |
2757 | + const QList<QtContacts::QContact> &updatedContacts, |
2758 | + const QStringList &removedContacts, |
2759 | + const QMap<QString, int> errorList, |
2760 | + Sync::SyncStatus status); |
2761 | + /* fast sync */ |
2762 | + void onRemoteContactsFetchedForFastSync(const QList<QtContacts::QContact> contacts, |
2763 | + Sync::SyncStatus status, |
2764 | + qreal progress); |
2765 | + void onContactsSavedForFastSync(const QList<QtContacts::QContact> &createdContacts, |
2766 | + const QList<QtContacts::QContact> &updatedContacts, |
2767 | + const QStringList &removedContacts, |
2768 | + const QMap<QString, int> errorMap, |
2769 | + Sync::SyncStatus status); |
2770 | + |
2771 | +}; |
2772 | + |
2773 | +#endif // UCONTACTCLIENT_H |
2774 | |
2775 | === added file 'buteo-contact-client/UContactsCustomDetail.cpp' |
2776 | --- buteo-contact-client/UContactsCustomDetail.cpp 1970-01-01 00:00:00 +0000 |
2777 | +++ buteo-contact-client/UContactsCustomDetail.cpp 2015-09-24 17:49:24 +0000 |
2778 | @@ -0,0 +1,56 @@ |
2779 | +/* |
2780 | + * This file is part of buteo-sync-plugins-contacts package |
2781 | + * |
2782 | + * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). |
2783 | + * 2015 Canonical Ltd |
2784 | + * |
2785 | + * Contributors: Sateesh Kavuri <sateesh.kavuri@gmail.com> |
2786 | + * Mani Chandrasekar <maninc@gmail.com> |
2787 | + * Renato Araujo Oliveira Filho <renato.filho@canonical.com> |
2788 | + * |
2789 | + * This library is free software; you can redistribute it and/or |
2790 | + * modify it under the terms of the GNU Lesser General Public License |
2791 | + * version 2.1 as published by the Free Software Foundation. |
2792 | + * |
2793 | + * This library is distributed in the hope that it will be useful, but |
2794 | + * WITHOUT ANY WARRANTY; without even the implied warranty of |
2795 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
2796 | + * Lesser General Public License for more details. |
2797 | + * |
2798 | + * You should have received a copy of the GNU Lesser General Public |
2799 | + * License along with this library; if not, write to the Free Software |
2800 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
2801 | + * 02110-1301 USA |
2802 | + * |
2803 | + */ |
2804 | + |
2805 | +#include "UContactsCustomDetail.h" |
2806 | + |
2807 | +// Ubuntu fields |
2808 | +const QString UContactsCustomDetail::FieldContactETag = "X-GOOGLE-ETAG"; |
2809 | +const QString UContactsCustomDetail::FieldGroupMembershipInfo = "X-GROUP-ID"; |
2810 | +const QString UContactsCustomDetail::FieldRemoteId = "X-REMOTE-ID"; |
2811 | +const QString UContactsCustomDetail::FieldDeletedAt = "X-DELETED-AT"; |
2812 | +const QString UContactsCustomDetail::FieldCreatedAt = "X-CREATED-AT"; |
2813 | +const QString UContactsCustomDetail::FieldContactAvatarETag = "X-AVATAR-REV"; |
2814 | + |
2815 | +QContactExtendedDetail |
2816 | +UContactsCustomDetail::getCustomField(const QContact &contact, const QString &name) |
2817 | +{ |
2818 | + foreach (QContactExtendedDetail xd, contact.details<QContactExtendedDetail>()) { |
2819 | + if (xd.name() == name) { |
2820 | + return xd; |
2821 | + } |
2822 | + } |
2823 | + QContactExtendedDetail xd; |
2824 | + xd.setName(name); |
2825 | + return xd; |
2826 | +} |
2827 | + |
2828 | + |
2829 | +void UContactsCustomDetail::setCustomField(QtContacts::QContact &contact, const QString &name, const QVariant &value) |
2830 | +{ |
2831 | + QContactExtendedDetail xd = getCustomField(contact, name); |
2832 | + xd.setData(value); |
2833 | + contact.saveDetail(&xd); |
2834 | +} |
2835 | |
2836 | === added file 'buteo-contact-client/UContactsCustomDetail.h' |
2837 | --- buteo-contact-client/UContactsCustomDetail.h 1970-01-01 00:00:00 +0000 |
2838 | +++ buteo-contact-client/UContactsCustomDetail.h 2015-09-24 17:49:24 +0000 |
2839 | @@ -0,0 +1,52 @@ |
2840 | +/* |
2841 | + * This file is part of buteo-sync-plugins-contacts package |
2842 | + * |
2843 | + * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). |
2844 | + * 2015 Canonical Ltd |
2845 | + * |
2846 | + * Contributors: Sateesh Kavuri <sateesh.kavuri@gmail.com> |
2847 | + * Mani Chandrasekar <maninc@gmail.com> |
2848 | + * Renato Araujo Oliveira Filho <renato.filho@canonical.com> |
2849 | + * |
2850 | + * This library is free software; you can redistribute it and/or |
2851 | + * modify it under the terms of the GNU Lesser General Public License |
2852 | + * version 2.1 as published by the Free Software Foundation. |
2853 | + * |
2854 | + * This library is distributed in the hope that it will be useful, but |
2855 | + * WITHOUT ANY WARRANTY; without even the implied warranty of |
2856 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
2857 | + * Lesser General Public License for more details. |
2858 | + * |
2859 | + * You should have received a copy of the GNU Lesser General Public |
2860 | + * License along with this library; if not, write to the Free Software |
2861 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
2862 | + * 02110-1301 USA |
2863 | + * |
2864 | + */ |
2865 | + |
2866 | +#ifndef UCONTACTSCUSTOMDETAIL_H |
2867 | +#define UCONTACTSCUSTOMDETAIL_H |
2868 | + |
2869 | +#include <QString> |
2870 | + |
2871 | +#include <QContact> |
2872 | +#include <QContactExtendedDetail> |
2873 | + |
2874 | +QTCONTACTS_USE_NAMESPACE |
2875 | + |
2876 | +class UContactsCustomDetail |
2877 | +{ |
2878 | +public: |
2879 | + // Ubuntu fields |
2880 | + static const QString FieldContactETag; |
2881 | + static const QString FieldRemoteId; |
2882 | + static const QString FieldGroupMembershipInfo; |
2883 | + static const QString FieldDeletedAt; |
2884 | + static const QString FieldCreatedAt; |
2885 | + static const QString FieldContactAvatarETag; |
2886 | + |
2887 | + static QContactExtendedDetail getCustomField(const QContact &contact, const QString &name); |
2888 | + static void setCustomField(QContact &contact, const QString &name, const QVariant &value); |
2889 | +}; |
2890 | + |
2891 | +#endif // GCONTACTCUSTOMDETAIL_H |
2892 | |
2893 | === added directory 'cmake' |
2894 | === added file 'cmake/lcov.cmake' |
2895 | --- cmake/lcov.cmake 1970-01-01 00:00:00 +0000 |
2896 | +++ cmake/lcov.cmake 2015-09-24 17:49:24 +0000 |
2897 | @@ -0,0 +1,72 @@ |
2898 | +# - This module creates a new 'lcov' target which generates |
2899 | +# a coverage analysis html output. |
2900 | +# LCOV is a graphical front-end for GCC's coverage testing tool gcov. Please see |
2901 | +# http://ltp.sourceforge.net/coverage/lcov.php |
2902 | +# |
2903 | +# Usage: you must add an option to your CMakeLists.txt to build your application |
2904 | +# with coverage support. Then you need to include this file to the lcov target. |
2905 | +# |
2906 | +# Example: |
2907 | +# IF(BUILD_WITH_COVERAGE) |
2908 | +# SET(CMAKE_C_FLAGS "-g -O0 -Wall -fprofile-arcs -ftest-coverage") |
2909 | +# SET(CMAKE_CXX_FLAGS "-g -O0 -Wall -fprofile-arcs -ftest-coverage") |
2910 | +# SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage -lgcov") |
2911 | +# include(${CMAKE_SOURCE_DIR}/cmake/lcov.cmake) |
2912 | +# ENDIF(BUILD_WITH_COVERAGE) |
2913 | +#============================================================================= |
2914 | +# Copyright 2010 ascolab GmbH |
2915 | +# |
2916 | +# Distributed under the OSI-approved BSD License (the "License"); |
2917 | +# see accompanying file Copyright.txt for details. |
2918 | +# |
2919 | +# This software is distributed WITHOUT ANY WARRANTY; without even the |
2920 | +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
2921 | +# See the License for more information. |
2922 | +#============================================================================= |
2923 | +# (To distributed this file outside of CMake, substitute the full |
2924 | +# License text for the above reference.) |
2925 | + |
2926 | +set(REMOVE_PATTERN |
2927 | + q*.h |
2928 | + *.moc |
2929 | + moc_*.cpp |
2930 | + locale_facets.h |
2931 | + new |
2932 | + move.h |
2933 | + qcontactmemorybackend.cpp |
2934 | + limits |
2935 | + /usr/include/c++/4.9/bits/* |
2936 | + /usr/include/buteosyncfw5/* |
2937 | + ) |
2938 | + |
2939 | +## lcov target |
2940 | +ADD_CUSTOM_TARGET(lcov) |
2941 | +ADD_CUSTOM_COMMAND(TARGET lcov |
2942 | + COMMAND mkdir -p coverage |
2943 | + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} |
2944 | + ) |
2945 | +ADD_CUSTOM_COMMAND(TARGET lcov |
2946 | + COMMAND lcov --directory . --zerocounters |
2947 | + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} |
2948 | + ) |
2949 | +ADD_CUSTOM_COMMAND(TARGET lcov |
2950 | + COMMAND make test |
2951 | + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} |
2952 | + ) |
2953 | +ADD_CUSTOM_COMMAND(TARGET lcov |
2954 | + COMMAND lcov --directory . --capture --output-file ./coverage/stap_all.info --no-checksum --compat-libtool |
2955 | + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} |
2956 | + ) |
2957 | +ADD_CUSTOM_COMMAND(TARGET lcov |
2958 | + COMMAND lcov --directory . -r ./coverage/stap_all.info ${REMOVE_PATTERN} --output-file ./coverage/stap.info |
2959 | + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} |
2960 | + ) |
2961 | +ADD_CUSTOM_COMMAND(TARGET lcov |
2962 | + COMMAND genhtml -o ./coverage --title "Code Coverage" --legend --show-details --demangle-cpp ./coverage/stap.info |
2963 | + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} |
2964 | + ) |
2965 | +ADD_CUSTOM_COMMAND(TARGET lcov |
2966 | + COMMAND echo "Open ${CMAKE_BINARY_DIR}/coverage/index.html to view the coverage analysis results." |
2967 | + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} |
2968 | + ) |
2969 | + |
2970 | |
2971 | === added file 'cmake_uninstall.cmake.in' |
2972 | --- cmake_uninstall.cmake.in 1970-01-01 00:00:00 +0000 |
2973 | +++ cmake_uninstall.cmake.in 2015-09-24 17:49:24 +0000 |
2974 | @@ -0,0 +1,21 @@ |
2975 | +IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") |
2976 | + MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") |
2977 | +ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") |
2978 | + |
2979 | +FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) |
2980 | +STRING(REGEX REPLACE "\n" ";" files "${files}") |
2981 | +FOREACH(file ${files}) |
2982 | + MESSAGE(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") |
2983 | + IF(EXISTS "$ENV{DESTDIR}${file}") |
2984 | + EXEC_PROGRAM( |
2985 | + "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" |
2986 | + OUTPUT_VARIABLE rm_out |
2987 | + RETURN_VALUE rm_retval |
2988 | + ) |
2989 | + IF(NOT "${rm_retval}" STREQUAL 0) |
2990 | + MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") |
2991 | + ENDIF(NOT "${rm_retval}" STREQUAL 0) |
2992 | + ELSE(EXISTS "$ENV{DESTDIR}${file}") |
2993 | + MESSAGE(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") |
2994 | + ENDIF(EXISTS "$ENV{DESTDIR}${file}") |
2995 | +ENDFOREACH(file) |
2996 | |
2997 | === added file 'config.h.in' |
2998 | --- config.h.in 1970-01-01 00:00:00 +0000 |
2999 | +++ config.h.in 2015-09-24 17:49:24 +0000 |
3000 | @@ -0,0 +1,9 @@ |
3001 | +#ifndef __CONFIG_H__ |
3002 | +#define __CONFIG_H__ |
3003 | + |
3004 | +#include <QString> |
3005 | + |
3006 | +const QString GOOGLE_ETAG_DETAIL ("X-GOOGLE-ETAG"); |
3007 | +const QString QCONTACTS_BACKEND_NAME ("galera"); |
3008 | + |
3009 | +#endif |
3010 | |
3011 | === added directory 'google' |
3012 | === added file 'google/CMakeLists.txt' |
3013 | --- google/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
3014 | +++ google/CMakeLists.txt 2015-09-24 17:49:24 +0000 |
3015 | @@ -0,0 +1,87 @@ |
3016 | +project(buteo-contact-google) |
3017 | +set(GOOGLE_CONTACTS_CLIENT googlecontacts-client) |
3018 | +set(GOOGLE_CONTACTS_LIB googlecontacts-lib) |
3019 | + |
3020 | +include_directories( |
3021 | + ${CMAKE_BINARY_DIR} |
3022 | + ${buteo-contact-client_SOURCE_DIR} |
3023 | + ${ACCOUNTS_INCLUDE_DIRS} |
3024 | + ${BUTEOSYNCFW_INCLUDE_DIRS} |
3025 | + ${LIBSIGNON_INCLUDE_DIRS} |
3026 | +) |
3027 | + |
3028 | +set(GOOGLE_CONTACTS_LIB_SRCS |
3029 | + atom_global.h |
3030 | + buteo-gcontact-plugin_global.h |
3031 | + buteosyncfw_p.h |
3032 | + GConfig.h |
3033 | + GConfig.cpp |
3034 | + GContactAtom.h |
3035 | + GContactAtom.cpp |
3036 | + GContactImageDownloader.h |
3037 | + GContactImageDownloader.cpp |
3038 | + GContactStream.h |
3039 | + GContactStream.cpp |
3040 | + GRemoteSource.h |
3041 | + GRemoteSource.cpp |
3042 | +) |
3043 | + |
3044 | +add_library(${GOOGLE_CONTACTS_LIB} STATIC |
3045 | + ${GOOGLE_CONTACTS_LIB_SRCS} |
3046 | +) |
3047 | + |
3048 | +target_link_libraries(${GOOGLE_CONTACTS_LIB} |
3049 | + ${ACCOUNTS_LIBRARIES} |
3050 | + ${BUTEOSYNCFW_LIBRARIES} |
3051 | + ${LIBSIGNON_LIBRARIES} |
3052 | + ubuntu-contact-client |
3053 | +) |
3054 | + |
3055 | +qt5_use_modules(${GOOGLE_CONTACTS_LIB} Core Network Contacts) |
3056 | + |
3057 | +# Buteo oop plugin |
3058 | +add_definitions(-DCLASSNAME=GContactsClient) |
3059 | +add_definitions(-DCLASSNAME_H=\"GContactsClient.h\") |
3060 | +add_definitions(-DCLIENT_PLUGIN) |
3061 | +set(GOOGLE_CONTACT_BUTEO_SRCS |
3062 | + ${BUTEOSYNCFW_INCLUDEDIR}/ButeoPluginIfaceAdaptor.h |
3063 | + ${BUTEOSYNCFW_INCLUDEDIR}/PluginCbImpl.h |
3064 | + ${BUTEOSYNCFW_INCLUDEDIR}/PluginServiceObj.h |
3065 | + ${BUTEOSYNCFW_INCLUDEDIR}/ButeoPluginIfaceAdaptor.cpp |
3066 | + ${BUTEOSYNCFW_INCLUDEDIR}/PluginCbImpl.cpp |
3067 | + ${BUTEOSYNCFW_INCLUDEDIR}/PluginServiceObj.cpp |
3068 | + ${BUTEOSYNCFW_INCLUDEDIR}/plugin_main.cpp |
3069 | +) |
3070 | + |
3071 | +set(GOOGLE_CONTACTS_CLIENT_SRCS |
3072 | + GContactsClient.h |
3073 | + GContactsClient.cpp |
3074 | + # real implementation |
3075 | + GTransport.h |
3076 | + GTransport.cpp |
3077 | + GContactImageUploader.cpp |
3078 | + GContactImageUploader.h |
3079 | + ${GOOGLE_CONTACT_BUTEO_SRCS} |
3080 | +) |
3081 | + |
3082 | +add_executable(${GOOGLE_CONTACTS_CLIENT} |
3083 | + ${GOOGLE_CONTACTS_CLIENT_SRCS} |
3084 | +) |
3085 | + |
3086 | +target_link_libraries(${GOOGLE_CONTACTS_CLIENT} |
3087 | + ${ACCOUNTS_LIBRARIES} |
3088 | + ${BUTEOSYNCFW_LIBRARIES} |
3089 | + ${LIBSIGNON_LIBRARIES} |
3090 | + ${GOOGLE_CONTACTS_LIB} |
3091 | + ubuntu-contact-client |
3092 | +) |
3093 | + |
3094 | +qt5_use_modules(${GOOGLE_CONTACTS_CLIENT} Core DBus Network Contacts) |
3095 | + |
3096 | +install(TARGETS ${GOOGLE_CONTACTS_CLIENT} |
3097 | + RUNTIME DESTINATION "${BUTEOSYNCFW_PLUGIN_PATH}/oopp" |
3098 | +) |
3099 | + |
3100 | +add_subdirectory(xmls) |
3101 | + |
3102 | + |
3103 | |
3104 | === added file 'google/GConfig.cpp' |
3105 | --- google/GConfig.cpp 1970-01-01 00:00:00 +0000 |
3106 | +++ google/GConfig.cpp 2015-09-24 17:49:24 +0000 |
3107 | @@ -0,0 +1,52 @@ |
3108 | +/* |
3109 | + * This file is part of buteo-sync-plugins-contacts package |
3110 | + * |
3111 | + * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). |
3112 | + * 2015 Canonical Ltd |
3113 | + * |
3114 | + * Contributors: Sateesh Kavuri <sateesh.kavuri@gmail.com> |
3115 | + * Mani Chandrasekar <maninc@gmail.com> |
3116 | + * Renato Araujo Oliveira Filho <renato.filho@canonical.com> |
3117 | + * |
3118 | + * This library is free software; you can redistribute it and/or |
3119 | + * modify it under the terms of the GNU Lesser General Public License |
3120 | + * version 2.1 as published by the Free Software Foundation. |
3121 | + * |
3122 | + * This library is distributed in the hope that it will be useful, but |
3123 | + * WITHOUT ANY WARRANTY; without even the implied warranty of |
3124 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
3125 | + * Lesser General Public License for more details. |
3126 | + * |
3127 | + * You should have received a copy of the GNU Lesser General Public |
3128 | + * License along with this library; if not, write to the Free Software |
3129 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
3130 | + * 02110-1301 USA |
3131 | + * |
3132 | + */ |
3133 | + |
3134 | +#include "GConfig.h" |
3135 | + |
3136 | +const int GConfig::MAX_RESULTS = 30; |
3137 | +const QString GConfig::SCOPE_URL = "https://www.google.com/m8/feeds/"; |
3138 | +const QString GConfig::GCONTACT_URL = SCOPE_URL + "/contacts/default/"; |
3139 | + |
3140 | +const QString GConfig::GDATA_VERSION_TAG = "GData-Version"; |
3141 | +const QString GConfig::GDATA_VERSION = "3.0"; |
3142 | +const QString GConfig::G_DELETE_OVERRIDE_HEADER = "X-HTTP-Method-Override: DELETE"; |
3143 | +const QString GConfig::G_ETAG_HEADER = "If-Match"; |
3144 | +const QString GConfig::G_AUTH_HEADER = "Authorization"; |
3145 | + |
3146 | +/* Query parameters */ |
3147 | +const QString GConfig::QUERY_TAG = "q"; |
3148 | +const QString GConfig::MAX_RESULTS_TAG = "max-results"; |
3149 | +const QString GConfig::START_INDEX_TAG = "start-index"; |
3150 | +const QString GConfig::UPDATED_MIN_TAG = "updated-min"; |
3151 | +const QString GConfig::ORDERBY_TAG = "orderby"; |
3152 | +const QString GConfig::SHOW_DELETED_TAG = "showdeleted"; |
3153 | +const QString GConfig::REQUIRE_ALL_DELETED = "requirealldeleted"; |
3154 | +const QString GConfig::SORTORDER_TAG = "sortorder"; |
3155 | +const QString GConfig::GROUP_MY_CONTACTS_ID = "6"; |
3156 | + |
3157 | +const QString GConfig::PHOTO_TAG = "photos"; |
3158 | +const QString GConfig::MEDIA_TAG = "media"; |
3159 | +const QString GConfig::BATCH_TAG = "batch"; |
3160 | |
3161 | === added file 'google/GConfig.h' |
3162 | --- google/GConfig.h 1970-01-01 00:00:00 +0000 |
3163 | +++ google/GConfig.h 2015-09-24 17:49:24 +0000 |
3164 | @@ -0,0 +1,71 @@ |
3165 | +/* |
3166 | + * This file is part of buteo-sync-plugins-contacts package |
3167 | + * |
3168 | + * Copyright (C) 2013 Jolla Ltd. and/or its subsidiary(-ies). |
3169 | + * 2015 Canonical Ltd |
3170 | + * |
3171 | + * Contributors: Sateesh Kavuri <sateesh.kavuri@gmail.com> |
3172 | + * Mani Chandrasekar <maninc@gmail.com> |
3173 | + * Renato Araujo Oliveira Filho <renato.filho@canonical.com> |
3174 | + * |
3175 | + * This library is free software; you can redistribute it and/or |
3176 | + * modify it under the terms of the GNU Lesser General Public License |
3177 | + * version 2.1 as published by the Free Software Foundation. |
3178 | + * |
3179 | + * This library is distributed in the hope that it will be useful, but |
3180 | + * WITHOUT ANY WARRANTY; without even the implied warranty of |
3181 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
3182 | + * Lesser General Public License for more details. |
3183 | + * |
3184 | + * You should have received a copy of the GNU Lesser General Public |
3185 | + * License along with this library; if not, write to the Free Software |
3186 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
3187 | + * 02110-1301 USA |
3188 | + * |
3189 | + */ |
3190 | + |
3191 | +#ifndef GCONFIG_H |
3192 | +#define GCONFIG_H |
3193 | + |
3194 | +#include <QString> |
3195 | + |
3196 | +class GConfig |
3197 | +{ |
3198 | +public: |
3199 | + static const int MAX_RESULTS; |
3200 | + static const QString SCOPE_URL; |
3201 | + static const QString GCONTACT_URL; |
3202 | + |
3203 | + static const QString GDATA_VERSION_TAG; |
3204 | + static const QString GDATA_VERSION; |
3205 | + static const QString G_DELETE_OVERRIDE_HEADER; |
3206 | + static const QString G_ETAG_HEADER; |
3207 | + static const QString G_AUTH_HEADER; |
3208 | + |
3209 | + /* My Contacts Group */ |
3210 | + static const QString GROUP_MY_CONTACTS_ID; |
3211 | + |
3212 | + /* Query parameters */ |
3213 | + static const QString QUERY_TAG; |
3214 | + static const QString MAX_RESULTS_TAG; |
3215 | + static const QString START_INDEX_TAG; |
3216 | + static const QString UPDATED_MIN_TAG; |
3217 | + static const QString ORDERBY_TAG; |
3218 | + static const QString SHOW_DELETED_TAG; |
3219 | + static const QString REQUIRE_ALL_DELETED; |
3220 | + static const QString SORTORDER_TAG; |
3221 | + |
3222 | + static const QString PHOTO_TAG; |
3223 | + static const QString MEDIA_TAG; |
3224 | + static const QString BATCH_TAG; |
3225 | + |
3226 | + typedef enum |
3227 | + { |
3228 | + NONE = 0, |
3229 | + ADD, |
3230 | + UPDATE, |
3231 | + DELETE |
3232 | + } TRANSACTION_TYPE; |
3233 | +}; |
3234 | +#endif // GCONFIG_H |
3235 | + |
3236 | |
3237 | === added file 'google/GContactAtom.cpp' |
3238 | --- google/GContactAtom.cpp 1970-01-01 00:00:00 +0000 |
3239 | +++ google/GContactAtom.cpp 2015-09-24 17:49:24 +0000 |
3240 | @@ -0,0 +1,209 @@ |
3241 | +/**************************************************************************** |
3242 | + ** |
3243 | + ** Copyright (C) 2013-2014 Jolla Ltd. and/or its subsidiary(-ies). |
3244 | + ** Contact: Chris Adams <chris.adams@jolla.com> |
3245 | + ** |
3246 | + ** Contributors: Sateesh Kavuri <sateesh.kavuri@gmail.com> |
3247 | + ** Mani Chandrasekar <maninc@gmail.com> |
3248 | + ** Chris Adams <chris.adams@jolla.com> |
3249 | + ** |
3250 | + ** This program/library is free software; you can redistribute it and/or |
3251 | + ** modify it under the terms of the GNU Lesser General Public License |
3252 | + ** version 2.1 as published by the Free Software Foundation. |
3253 | + ** |
3254 | + ** This program/library is distributed in the hope that it will be useful, |
3255 | + ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
3256 | + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
3257 | + ** Lesser General Public License for more details. |
3258 | + ** |
3259 | + ** You should have received a copy of the GNU Lesser General Public |
3260 | + ** License along with this program/library; if not, write to the Free |
3261 | + ** Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
3262 | + ** 02110-1301 USA |
3263 | + ** |
3264 | + ****************************************************************************/ |
3265 | + |
3266 | +#include "GContactAtom.h" |
3267 | + |
3268 | +GoogleContactAtom::BatchOperationResponse::BatchOperationResponse() |
3269 | + : isError(false) |
3270 | +{ |
3271 | +} |
3272 | + |
3273 | +GoogleContactAtom::GoogleContactAtom() |
3274 | +{ |
3275 | +} |
3276 | + |
3277 | +void GoogleContactAtom::setAuthorEmail(const QString &authorEmail) |
3278 | +{ |
3279 | + mAuthorEmail = authorEmail; |
3280 | +} |
3281 | + |
3282 | +QString GoogleContactAtom::authorEmail() const |
3283 | +{ |
3284 | + return mAuthorEmail; |
3285 | +} |
3286 | + |
3287 | +void GoogleContactAtom::setAuthorName(const QString &authorName) |
3288 | +{ |
3289 | + mAuthorName = authorName; |
3290 | +} |
3291 | + |
3292 | +QString GoogleContactAtom::authorName() const |
3293 | +{ |
3294 | + return mAuthorName; |
3295 | +} |
3296 | + |
3297 | +void GoogleContactAtom::setId(const QString &id) |
3298 | +{ |
3299 | + mId = id; |
3300 | +} |
3301 | + |
3302 | +QString GoogleContactAtom::id() const |
3303 | +{ |
3304 | + return mId; |
3305 | +} |
3306 | + |
3307 | +void GoogleContactAtom::setUpdated(const QString &updated) |
3308 | +{ |
3309 | + mUpdated = updated; |
3310 | +} |
3311 | + |
3312 | +QString GoogleContactAtom::updated() const |
3313 | +{ |
3314 | + return mUpdated; |
3315 | +} |
3316 | + |
3317 | +void GoogleContactAtom::setCategory(const QString &schema, const QString &term) |
3318 | +{ |
3319 | + mCategorySchema = schema; |
3320 | + mCategoryTerm = term; |
3321 | +} |
3322 | + |
3323 | +QString GoogleContactAtom::categorySchema() const |
3324 | +{ |
3325 | + return mCategorySchema; |
3326 | +} |
3327 | + |
3328 | +QString GoogleContactAtom::categoryTerm() const |
3329 | +{ |
3330 | + return mCategoryTerm; |
3331 | +} |
3332 | + |
3333 | +void GoogleContactAtom::setTitle(const QString &title) |
3334 | +{ |
3335 | + mTitle = title; |
3336 | +} |
3337 | + |
3338 | +QString GoogleContactAtom::title() const |
3339 | +{ |
3340 | + return mTitle; |
3341 | +} |
3342 | + |
3343 | +void GoogleContactAtom::setGenerator(const QString &name, const QString &version, const QString &uri) |
3344 | +{ |
3345 | + mGeneratorName = name; |
3346 | + mGeneratorVersion = version; |
3347 | + mGeneratorUri = uri; |
3348 | +} |
3349 | + |
3350 | +void GoogleContactAtom::setContent(const QString ¬e, const QString &type) |
3351 | +{ |
3352 | + Q_UNUSED(note) |
3353 | + Q_UNUSED(type) |
3354 | +} |
3355 | + |
3356 | +QString GoogleContactAtom::generatorName() const |
3357 | +{ |
3358 | + return mGeneratorName; |
3359 | +} |
3360 | + |
3361 | +QString GoogleContactAtom::generatorVersion() const |
3362 | +{ |
3363 | + return mGeneratorVersion; |
3364 | +} |
3365 | + |
3366 | +QString GoogleContactAtom::generatorUri() const |
3367 | +{ |
3368 | + return mGeneratorUri; |
3369 | +} |
3370 | + |
3371 | +void GoogleContactAtom::setTotalResults(int totalResults) |
3372 | +{ |
3373 | + mTotalResults = totalResults; |
3374 | +} |
3375 | + |
3376 | +int GoogleContactAtom::totalResults() const |
3377 | +{ |
3378 | + return mTotalResults; |
3379 | +} |
3380 | + |
3381 | +void GoogleContactAtom::setStartIndex(int startIndex) |
3382 | +{ |
3383 | + mStartIndex = startIndex; |
3384 | +} |
3385 | + |
3386 | +int GoogleContactAtom::startIndex() const |
3387 | +{ |
3388 | + return mStartIndex; |
3389 | +} |
3390 | + |
3391 | +void GoogleContactAtom::setItemsPerPage(int itemsPerPage) |
3392 | +{ |
3393 | + mItemsPerPage = itemsPerPage; |
3394 | +} |
3395 | + |
3396 | +int GoogleContactAtom::itemsPerPage() const |
3397 | +{ |
3398 | + return mItemsPerPage; |
3399 | +} |
3400 | + |
3401 | +void GoogleContactAtom::addBatchOperationResponse(const QString &operationId, GoogleContactAtom::BatchOperationResponse response) |
3402 | +{ |
3403 | + mBatchOperationResponses.insert(operationId, response); |
3404 | +} |
3405 | + |
3406 | +QMap<QString, GoogleContactAtom::BatchOperationResponse> GoogleContactAtom::batchOperationResponses() const |
3407 | +{ |
3408 | + return mBatchOperationResponses; |
3409 | +} |
3410 | + |
3411 | +void GoogleContactAtom::addEntryContact(const QContact &entryContact, const QStringList &unsupportedElements) |
3412 | +{ |
3413 | + mContactList.append(qMakePair(entryContact, unsupportedElements)); |
3414 | +} |
3415 | + |
3416 | +QList<QPair<QContact, QStringList> > GoogleContactAtom::entryContacts() const |
3417 | +{ |
3418 | + return mContactList; |
3419 | +} |
3420 | + |
3421 | +void GoogleContactAtom::addDeletedEntryContact(const QContact &deletedContact) |
3422 | +{ |
3423 | + mDeletedContactList.append(deletedContact); |
3424 | +} |
3425 | + |
3426 | +QList<QContact> GoogleContactAtom::deletedEntryContacts() const |
3427 | +{ |
3428 | + return mDeletedContactList; |
3429 | +} |
3430 | + |
3431 | +void GoogleContactAtom::addEntrySystemGroup(const QString &systemGroupId, const QString &systemGroupAtomId) |
3432 | +{ |
3433 | + mSystemGroupAtomIds.insert(systemGroupId, systemGroupAtomId); |
3434 | +} |
3435 | + |
3436 | +QMap<QString, QString> GoogleContactAtom::entrySystemGroups() const |
3437 | +{ |
3438 | + return mSystemGroupAtomIds; |
3439 | +} |
3440 | + |
3441 | +void GoogleContactAtom::setNextEntriesUrl(const QString &nextUrl) |
3442 | +{ |
3443 | + mNextEntriesUrl = nextUrl; |
3444 | +} |
3445 | + |
3446 | +QString GoogleContactAtom::nextEntriesUrl() const |
3447 | +{ |
3448 | + return mNextEntriesUrl; |
3449 | +} |
3450 | |
3451 | === added file 'google/GContactAtom.h' |
3452 | --- google/GContactAtom.h 1970-01-01 00:00:00 +0000 |
3453 | +++ google/GContactAtom.h 2015-09-24 17:49:24 +0000 |
3454 | @@ -0,0 +1,138 @@ |
3455 | +/**************************************************************************** |
3456 | + ** |
3457 | + ** Copyright (C) 2013-2014 Jolla Ltd. and/or its subsidiary(-ies). |
3458 | + ** Contact: Chris Adams <chris.adams@jolla.com> |
3459 | + ** |
3460 | + ** Contributors: Sateesh Kavuri <sateesh.kavuri@gmail.com> |
3461 | + ** Chris Adams <chris.adams@jolla.com> |
3462 | + ** |
3463 | + ** This program/library is free software; you can redistribute it and/or |
3464 | + ** modify it under the terms of the GNU Lesser General Public License |
3465 | + ** version 2.1 as published by the Free Software Foundation. |
3466 | + ** |
3467 | + ** This program/library is distributed in the hope that it will be useful, |
3468 | + ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
3469 | + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
3470 | + ** Lesser General Public License for more details. |
3471 | + ** |
3472 | + ** You should have received a copy of the GNU Lesser General Public |
3473 | + ** License along with this program/library; if not, write to the Free |
3474 | + ** Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
3475 | + ** 02110-1301 USA |
3476 | + ** |
3477 | + ****************************************************************************/ |
3478 | + |
3479 | +#ifndef GOOGLECONTACTATOM_H |
3480 | +#define GOOGLECONTACTATOM_H |
3481 | + |
3482 | +#include <QMetaEnum> |
3483 | +#include <QMap> |
3484 | +#include <QList> |
3485 | +#include <QXmlStreamWriter> |
3486 | + |
3487 | +#include <QContact> |
3488 | + |
3489 | +QTCONTACTS_USE_NAMESPACE |
3490 | + |
3491 | +class GoogleContactAtom { |
3492 | +public: |
3493 | + GoogleContactAtom(); |
3494 | + |
3495 | + void setAuthorName(const QString &authorName); |
3496 | + QString authorName() const; |
3497 | + |
3498 | + void setAuthorEmail(const QString &authorEmail); |
3499 | + QString authorEmail() const; |
3500 | + |
3501 | + void setId(const QString &id); |
3502 | + QString id() const; |
3503 | + |
3504 | + void setUpdated(const QString &updated); |
3505 | + QString updated() const; |
3506 | + |
3507 | + void setCategory(const QString &schema = QStringLiteral("http://schemas.google.com/g/2005#kind"), |
3508 | + const QString &term = QStringLiteral("http://schemas.google.com/contact/2008#contact")); |
3509 | + QString categorySchema() const; |
3510 | + QString categoryTerm() const; |
3511 | + |
3512 | + void setTitle(const QString &title); |
3513 | + QString title() const; |
3514 | + |
3515 | + void setContent(const QString ¬e, const QString &type = QStringLiteral("text")); |
3516 | + |
3517 | + void setGenerator(const QString &name = QStringLiteral("Contacts"), |
3518 | + const QString &version = QStringLiteral("1.0"), |
3519 | + const QString &uri = QStringLiteral("https://sailfishos.org")); |
3520 | + QString generatorName() const; |
3521 | + QString generatorVersion() const; |
3522 | + QString generatorUri() const; |
3523 | + |
3524 | + void setTotalResults(int totalResults); |
3525 | + int totalResults() const; |
3526 | + |
3527 | + void setStartIndex(int startIndex); |
3528 | + int startIndex() const; |
3529 | + |
3530 | + void setItemsPerPage(int itemsPerPage); |
3531 | + int itemsPerPage() const; |
3532 | + |
3533 | + void addEntryContact(const QContact &contact, const QStringList &unsupportedElements); |
3534 | + QList<QPair<QContact, QStringList> > entryContacts() const; |
3535 | + void addDeletedEntryContact(const QContact &contact); |
3536 | + QList<QContact> deletedEntryContacts() const; |
3537 | + |
3538 | + void addEntrySystemGroup(const QString &systemGroupId, const QString &systemGroupAtomId); |
3539 | + QMap<QString, QString> entrySystemGroups() const; |
3540 | + |
3541 | + void setNextEntriesUrl(const QString &nextUrl); |
3542 | + QString nextEntriesUrl() const; |
3543 | + |
3544 | + class BatchOperationResponse { |
3545 | + public: |
3546 | + BatchOperationResponse(); |
3547 | + QString operationId; |
3548 | + QString type; |
3549 | + QString code; |
3550 | + QString reason; |
3551 | + QString reasonDescription; |
3552 | + QString contactGuid; |
3553 | + QString eTag; |
3554 | + bool isError; |
3555 | + }; |
3556 | + void addBatchOperationResponse(const QString &operationId, BatchOperationResponse response); |
3557 | + QMap<QString, BatchOperationResponse> batchOperationResponses() const; |
3558 | + |
3559 | +private: |
3560 | + QString mAuthorEmail; |
3561 | + QString mAuthorName; |
3562 | + QString mCategory; |
3563 | + QString mCategorySchema; |
3564 | + QString mCategoryTerm; |
3565 | + QString mContributor; |
3566 | + QString mGeneratorName; |
3567 | + QString mGeneratorVersion; |
3568 | + QString mGeneratorUri; |
3569 | + QString mIcon; |
3570 | + QString mId; |
3571 | + QString mLink; |
3572 | + QString mLogo; |
3573 | + QString mRights; |
3574 | + QString mSubtitle; |
3575 | + QString mTitle; |
3576 | + QString mUpdated; |
3577 | + |
3578 | + int mTotalResults; |
3579 | + int mStartIndex; |
3580 | + int mItemsPerPage; |
3581 | + |
3582 | + QMap<QString, BatchOperationResponse> mBatchOperationResponses; |
3583 | + |
3584 | + QList<QContact> mDeletedContactList; |
3585 | + QList<QPair<QContact, QStringList> > mContactList; |
3586 | + |
3587 | + QMap<QString, QString> mSystemGroupAtomIds; |
3588 | + |
3589 | + QString mNextEntriesUrl; |
3590 | +}; |
3591 | + |
3592 | +#endif // GOOGLECONTACTATOM_H |
3593 | |
3594 | === added file 'google/GContactGroupsMap.cpp' |
3595 | --- google/GContactGroupsMap.cpp 1970-01-01 00:00:00 +0000 |
3596 | +++ google/GContactGroupsMap.cpp 2015-09-24 17:49:24 +0000 |
3597 | @@ -0,0 +1,122 @@ |
3598 | +/**************************************************************************** |
3599 | + ** |
3600 | + ** Copyright (C) 2015 Canonical Ltd. |
3601 | + ** |
3602 | + ** Contact: Renato Araujo Oliveira Filho <renato.filho@canonical.com> |
3603 | + ** |
3604 | + ** This program/library is free software; you can redistribute it and/or |
3605 | + ** modify it under the terms of the GNU Lesser General Public License |
3606 | + ** version 2.1 as published by the Free Software Foundation. |
3607 | + ** |
3608 | + ** This program/library is distributed in the hope that it will be useful, |
3609 | + ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
3610 | + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
3611 | + ** Lesser General Public License for more details. |
3612 | + ** |
3613 | + ** You should have received a copy of the GNU Lesser General Public |
3614 | + ** License along with this program/library; if not, write to the Free |
3615 | + ** Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
3616 | + ** 02110-1301 USA |
3617 | + ** |
3618 | + ****************************************************************************/ |
3619 | + |
3620 | +#include "GContactGroupsMap.h" |
3621 | +#include <LogMacros.h> |
3622 | + |
3623 | +#include <QNetworkRequest> |
3624 | +#include <QNetworkReply> |
3625 | +#include <QNetworkAccessManager> |
3626 | +#include <QTemporaryFile> |
3627 | + |
3628 | +GContactGroupMap::GContactGroupMap(const QString &authToken, QObject *parent) |
3629 | + : QObject(parent), |
3630 | + mNetworkAccessManager(new QNetworkAccessManager), |
3631 | + mAuthToken(authToken) |
3632 | +{ |
3633 | +} |
3634 | + |
3635 | +GContactGroupMap::~GContactGroupMap() |
3636 | +{ |
3637 | +} |
3638 | + |
3639 | +void GContactGroupMap::reload() |
3640 | +{ |
3641 | + connect(networkAccessManager, |
3642 | + SIGNAL(finished(QNetworkReply*)), |
3643 | + SLOT(onRequestFinished(QNetworkReply*)), |
3644 | + Qt::QueuedConnection); |
3645 | + |
3646 | + QNetworkRequest request(mQueue.takeFirst()); |
3647 | + request.setRawHeader("GData-Version", "3.0"); |
3648 | + request.setRawHeader(QString(QLatin1String("Authorization")).toUtf8(), |
3649 | + QString(QLatin1String("Bearer ") + mAuthToken).toUtf8()); |
3650 | + QNetworkReply *reply = networkAccessManager->get(request); |
3651 | +} |
3652 | + |
3653 | +void GContactGroupMap::push(const QUrl &imgUrl) |
3654 | +{ |
3655 | + mQueue.push_back(imgUrl); |
3656 | +} |
3657 | + |
3658 | +QMap<QUrl, QUrl> GContactImageDownloader::donwloaded() |
3659 | +{ |
3660 | + return mResults; |
3661 | +} |
3662 | + |
3663 | +void GContactImageDownloader::exec() |
3664 | +{ |
3665 | + QNetworkAccessManager *networkAccessManager = new QNetworkAccessManager; |
3666 | + |
3667 | + |
3668 | + QEventLoop eventLoop; |
3669 | + mEventLoop = &eventLoop; |
3670 | + |
3671 | + while(!mQueue.isEmpty()) { |
3672 | + QNetworkRequest request(mQueue.takeFirst()); |
3673 | + request.setRawHeader("GData-Version", "3.0"); |
3674 | + request.setRawHeader(QString(QLatin1String("Authorization")).toUtf8(), |
3675 | + QString(QLatin1String("Bearer ") + mAuthToken).toUtf8()); |
3676 | + QNetworkReply *reply = networkAccessManager->get(request); |
3677 | + |
3678 | + // wait for the download to finish |
3679 | + eventLoop.exec(); |
3680 | + |
3681 | + // should we abort? |
3682 | + if (mAbort) { |
3683 | + break; |
3684 | + } |
3685 | + } |
3686 | + delete networkAccessManager; |
3687 | +} |
3688 | + |
3689 | +void GContactImageDownloader::onRequestFinished(QNetworkReply *reply) |
3690 | +{ |
3691 | + if (reply->error() != QNetworkReply::NoError) { |
3692 | + LOG_WARNING("Fail to download avatar:" << reply->errorString()); |
3693 | + emit donwloadError(reply->url(), reply->errorString()); |
3694 | + } else { |
3695 | + QUrl localFile(saveImage(reply->url(), reply->readAll())); |
3696 | + mResults.insert(reply->url(), localFile); |
3697 | + emit downloadFinished(reply->url(), localFile); |
3698 | + } |
3699 | + |
3700 | + if (mEventLoop) { |
3701 | + mEventLoop->quit(); |
3702 | + } |
3703 | +} |
3704 | + |
3705 | +QUrl GContactImageDownloader::saveImage(const QUrl &remoteFile, const QByteArray &imgData) |
3706 | +{ |
3707 | + Q_UNUSED(remoteFile); |
3708 | + |
3709 | + QTemporaryFile tmp; |
3710 | + if (tmp.open()) { |
3711 | + tmp.write(imgData); |
3712 | + tmp.setAutoRemove(false); |
3713 | + tmp.close(); |
3714 | + mTempFiles << tmp.fileName(); |
3715 | + return QUrl::fromLocalFile(tmp.fileName()); |
3716 | + } |
3717 | + return QUrl(); |
3718 | +} |
3719 | + |
3720 | |
3721 | === added file 'google/GContactGroupsMap.h' |
3722 | --- google/GContactGroupsMap.h 1970-01-01 00:00:00 +0000 |
3723 | +++ google/GContactGroupsMap.h 2015-09-24 17:49:24 +0000 |
3724 | @@ -0,0 +1,60 @@ |
3725 | +/**************************************************************************** |
3726 | + ** |
3727 | + ** Copyright (C) 2015 Canonical Ltd. |
3728 | + ** |
3729 | + ** Contact: Renato Araujo Oliveira Filho <renato.filho@canonical.com> |
3730 | + ** |
3731 | + ** This program/library is free software; you can redistribute it and/or |
3732 | + ** modify it under the terms of the GNU Lesser General Public License |
3733 | + ** version 2.1 as published by the Free Software Foundation. |
3734 | + ** |
3735 | + ** This program/library is distributed in the hope that it will be useful, |
3736 | + ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
3737 | + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
3738 | + ** Lesser General Public License for more details. |
3739 | + ** |
3740 | + ** You should have received a copy of the GNU Lesser General Public |
3741 | + ** License along with this program/library; if not, write to the Free |
3742 | + ** Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
3743 | + ** 02110-1301 USA |
3744 | + ** |
3745 | + ****************************************************************************/ |
3746 | + |
3747 | +#ifndef GOOGLECONTACTGRUOPMAP_H |
3748 | +#define GOOGLECONTACTGRUOPMAP_H |
3749 | + |
3750 | +#include <QObject> |
3751 | +#include <QString> |
3752 | +#include <QUrl> |
3753 | +#include <QQueue> |
3754 | +#include <QThread> |
3755 | +#include <QMap> |
3756 | +#include <QMutex> |
3757 | +#include <QEventLoop> |
3758 | +#include <QtNetwork/QNetworkAccessManager> |
3759 | + |
3760 | +class GTransport; |
3761 | + |
3762 | +class GContactGroupMap: public QObject |
3763 | +{ |
3764 | + Q_OBJECT |
3765 | + |
3766 | +public: |
3767 | + explicit GContactGroupMap(const QString &authToken, QObject *parent = 0); |
3768 | + ~GContactGroupMap(); |
3769 | + |
3770 | + void reload(); |
3771 | + |
3772 | +signals: |
3773 | + void updated(); |
3774 | + |
3775 | +private slots: |
3776 | + void onRequestFinished(QNetworkReply *reply); |
3777 | + |
3778 | +private: |
3779 | + QScopedPointer<QNetworkAccessManager> mNetworkAccessManager; |
3780 | + QString mAuthToken; |
3781 | + QMap<QString, QString> mGroups; |
3782 | +}; |
3783 | + |
3784 | +#endif // GOOGLECONTACTGRUOPMAP_H |
3785 | |
3786 | === added file 'google/GContactImageDownloader.cpp' |
3787 | --- google/GContactImageDownloader.cpp 1970-01-01 00:00:00 +0000 |
3788 | +++ google/GContactImageDownloader.cpp 2015-09-24 17:49:24 +0000 |
3789 | @@ -0,0 +1,117 @@ |
3790 | +/**************************************************************************** |
3791 | + ** |
3792 | + ** Copyright (C) 2015 Canonical Ltd. |
3793 | + ** |
3794 | + ** Contact: Renato Araujo Oliveira Filho <renato.filho@canonical.com> |
3795 | + ** |
3796 | + ** This program/library is free software; you can redistribute it and/or |
3797 | + ** modify it under the terms of the GNU Lesser General Public License |
3798 | + ** version 2.1 as published by the Free Software Foundation. |
3799 | + ** |
3800 | + ** This program/library is distributed in the hope that it will be useful, |
3801 | + ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
3802 | + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
3803 | + ** Lesser General Public License for more details. |
3804 | + ** |
3805 | + ** You should have received a copy of the GNU Lesser General Public |
3806 | + ** License along with this program/library; if not, write to the Free |
3807 | + ** Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
3808 | + ** 02110-1301 USA |
3809 | + ** |
3810 | + ****************************************************************************/ |
3811 | + |
3812 | +#include "GContactImageDownloader.h" |
3813 | +#include "GTransport.h" |
3814 | + |
3815 | +#include <LogMacros.h> |
3816 | + |
3817 | +#include <QNetworkRequest> |
3818 | +#include <QNetworkReply> |
3819 | +#include <QNetworkAccessManager> |
3820 | +#include <QTemporaryFile> |
3821 | + |
3822 | +GContactImageDownloader::GContactImageDownloader(const QString &authToken, QObject *parent) |
3823 | + : QObject(parent), |
3824 | + mEventLoop(0), |
3825 | + mAuthToken(authToken), |
3826 | + mAbort(false) |
3827 | +{ |
3828 | +} |
3829 | + |
3830 | +GContactImageDownloader::~GContactImageDownloader() |
3831 | +{ |
3832 | + foreach(const QString &file, mTempFiles) { |
3833 | + QFile::remove(file); |
3834 | + } |
3835 | +} |
3836 | + |
3837 | +void GContactImageDownloader::push(const QUrl &imgUrl) |
3838 | +{ |
3839 | + mQueue.push_back(imgUrl); |
3840 | +} |
3841 | + |
3842 | +QMap<QUrl, QUrl> GContactImageDownloader::donwloaded() |
3843 | +{ |
3844 | + return mResults; |
3845 | +} |
3846 | + |
3847 | +void GContactImageDownloader::exec() |
3848 | +{ |
3849 | + QNetworkAccessManager *networkAccessManager = new QNetworkAccessManager; |
3850 | + connect(networkAccessManager, |
3851 | + SIGNAL(finished(QNetworkReply*)), |
3852 | + SLOT(onRequestFinished(QNetworkReply*)), |
3853 | + Qt::QueuedConnection); |
3854 | + |
3855 | + QEventLoop eventLoop; |
3856 | + mEventLoop = &eventLoop; |
3857 | + |
3858 | + while(!mQueue.isEmpty()) { |
3859 | + QNetworkRequest request(mQueue.takeFirst()); |
3860 | + request.setRawHeader(QStringLiteral("GData-Version").toUtf8(), QStringLiteral("3.0").toUtf8()); |
3861 | + request.setRawHeader(QStringLiteral("Authorization").toUtf8(), |
3862 | + QStringLiteral("Bearer %1").arg(mAuthToken).toUtf8()); |
3863 | + networkAccessManager->get(request); |
3864 | + |
3865 | + // wait for the download to finish |
3866 | + eventLoop.exec(); |
3867 | + |
3868 | + // should we abort? |
3869 | + if (mAbort) { |
3870 | + break; |
3871 | + } |
3872 | + } |
3873 | + delete networkAccessManager; |
3874 | +} |
3875 | + |
3876 | +void GContactImageDownloader::onRequestFinished(QNetworkReply *reply) |
3877 | +{ |
3878 | + if (reply->error() != QNetworkReply::NoError) { |
3879 | + LOG_WARNING("Fail to download avatar:" << reply->errorString()); |
3880 | + emit donwloadError(reply->url(), reply->errorString()); |
3881 | + } else { |
3882 | + QUrl localFile(saveImage(reply->url(), reply->readAll())); |
3883 | + mResults.insert(reply->url(), localFile); |
3884 | + emit downloadFinished(reply->url(), localFile); |
3885 | + } |
3886 | + |
3887 | + if (mEventLoop) { |
3888 | + mEventLoop->quit(); |
3889 | + } |
3890 | +} |
3891 | + |
3892 | +QUrl GContactImageDownloader::saveImage(const QUrl &remoteFile, const QByteArray &imgData) |
3893 | +{ |
3894 | + Q_UNUSED(remoteFile); |
3895 | + |
3896 | + QTemporaryFile tmp; |
3897 | + if (tmp.open()) { |
3898 | + tmp.write(imgData); |
3899 | + tmp.setAutoRemove(false); |
3900 | + tmp.close(); |
3901 | + mTempFiles << tmp.fileName(); |
3902 | + return QUrl::fromLocalFile(tmp.fileName()); |
3903 | + } |
3904 | + return QUrl(); |
3905 | +} |
3906 | + |
3907 | |
3908 | === added file 'google/GContactImageDownloader.h' |
3909 | --- google/GContactImageDownloader.h 1970-01-01 00:00:00 +0000 |
3910 | +++ google/GContactImageDownloader.h 2015-09-24 17:49:24 +0000 |
3911 | @@ -0,0 +1,68 @@ |
3912 | +/**************************************************************************** |
3913 | + ** |
3914 | + ** Copyright (C) 2015 Canonical Ltd. |
3915 | + ** |
3916 | + ** Contact: Renato Araujo Oliveira Filho <renato.filho@canonical.com> |
3917 | + ** |
3918 | + ** This program/library is free software; you can redistribute it and/or |
3919 | + ** modify it under the terms of the GNU Lesser General Public License |
3920 | + ** version 2.1 as published by the Free Software Foundation. |
3921 | + ** |
3922 | + ** This program/library is distributed in the hope that it will be useful, |
3923 | + ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
3924 | + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
3925 | + ** Lesser General Public License for more details. |
3926 | + ** |
3927 | + ** You should have received a copy of the GNU Lesser General Public |
3928 | + ** License along with this program/library; if not, write to the Free |
3929 | + ** Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
3930 | + ** 02110-1301 USA |
3931 | + ** |
3932 | + ****************************************************************************/ |
3933 | + |
3934 | +#ifndef GOOGLECONTACTIMAGEDOWNLOADER_H |
3935 | +#define GOOGLECONTACTIMAGEDOWNLOADER_H |
3936 | + |
3937 | +#include <QObject> |
3938 | +#include <QString> |
3939 | +#include <QUrl> |
3940 | +#include <QQueue> |
3941 | +#include <QThread> |
3942 | +#include <QMap> |
3943 | +#include <QMutex> |
3944 | +#include <QEventLoop> |
3945 | +#include <QtNetwork/QNetworkAccessManager> |
3946 | + |
3947 | +class GTransport; |
3948 | + |
3949 | +class GContactImageDownloader: public QObject |
3950 | +{ |
3951 | + Q_OBJECT |
3952 | + |
3953 | +public: |
3954 | + explicit GContactImageDownloader(const QString &authToken, QObject *parent = 0); |
3955 | + ~GContactImageDownloader(); |
3956 | + |
3957 | + void push(const QUrl &imgUrl); |
3958 | + QMap<QUrl, QUrl> donwloaded(); |
3959 | + void exec(); |
3960 | + |
3961 | +signals: |
3962 | + void downloadFinished(const QUrl &imgUrl, const QUrl &localFile); |
3963 | + void donwloadError(const QUrl &imgUrl, const QString &error); |
3964 | + |
3965 | +private slots: |
3966 | + void onRequestFinished(QNetworkReply *reply); |
3967 | + |
3968 | +private: |
3969 | + QEventLoop *mEventLoop; |
3970 | + QQueue<QUrl> mQueue; |
3971 | + QString mAuthToken; |
3972 | + QMap<QUrl, QUrl> mResults; |
3973 | + bool mAbort; |
3974 | + QStringList mTempFiles; |
3975 | + |
3976 | + QUrl saveImage(const QUrl &remoteFile, const QByteArray &imgData); |
3977 | +}; |
3978 | + |
3979 | +#endif // GOOGLECONTACTIMAGEDOWNLOADER_H |
3980 | |
3981 | === added file 'google/GContactImageUploader.cpp' |
3982 | --- google/GContactImageUploader.cpp 1970-01-01 00:00:00 +0000 |
3983 | +++ google/GContactImageUploader.cpp 2015-09-24 17:49:24 +0000 |
3984 | @@ -0,0 +1,204 @@ |
3985 | +/**************************************************************************** |
3986 | + ** |
3987 | + ** Copyright (C) 2015 Canonical Ltd. |
3988 | + ** |
3989 | + ** Contact: Renato Araujo Oliveira Filho <renato.filho@canonical.com> |
3990 | + ** |
3991 | + ** This program/library is free software; you can redistribute it and/or |
3992 | + ** modify it under the terms of the GNU Lesser General Public License |
3993 | + ** version 2.1 as published by the Free Software Foundation. |
3994 | + ** |
3995 | + ** This program/library is distributed in the hope that it will be useful, |
3996 | + ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
3997 | + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
3998 | + ** Lesser General Public License for more details. |
3999 | + ** |
4000 | + ** You should have received a copy of the GNU Lesser General Public |
4001 | + ** License along with this program/library; if not, write to the Free |
4002 | + ** Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
4003 | + ** 02110-1301 USA |
4004 | + ** |
4005 | + ****************************************************************************/ |
4006 | + |
4007 | +#include "GContactImageUploader.h" |
4008 | +#include "GContactStream.h" |
4009 | +#include "GContactAtom.h" |
4010 | +#include "UContactsCustomDetail.h" |
4011 | + |
4012 | +#include <LogMacros.h> |
4013 | + |
4014 | +#include <QNetworkRequest> |
4015 | +#include <QNetworkReply> |
4016 | +#include <QNetworkAccessManager> |
4017 | +#include <QScopedPointer> |
4018 | +#include <QDomDocument> |
4019 | + |
4020 | +#define GOOGLE_URL "https://www.google.com/m8/feeds/contacts/default/full" |
4021 | +#define GOOGLE_PHOTO_URL "https://www.google.com/m8/feeds/photos/media/%1/%2" |
4022 | + |
4023 | +GContactImageUploader::GContactImageUploader(const QString &authToken, |
4024 | + const QString &accountId, |
4025 | + QObject *parent) |
4026 | + : QObject(parent), |
4027 | + mEventLoop(0), |
4028 | + mAuthToken(authToken), |
4029 | + mAccountId(accountId), |
4030 | + mAbort(false) |
4031 | +{ |
4032 | +} |
4033 | + |
4034 | +void GContactImageUploader::push(const QString &remoteId, const QUrl &imgUrl) |
4035 | +{ |
4036 | + mQueue.push_back(qMakePair(remoteId, imgUrl)); |
4037 | +} |
4038 | + |
4039 | +QMap<QString, GContactImageUploader::UploaderReply> GContactImageUploader::result() |
4040 | +{ |
4041 | + return mResults; |
4042 | +} |
4043 | + |
4044 | +/* |
4045 | + * This is a sync function since the contact sync process happen in a different |
4046 | + * thread we do not need this function to be async |
4047 | + */ |
4048 | +void GContactImageUploader::exec() |
4049 | +{ |
4050 | + if (mQueue.isEmpty()) { |
4051 | + return; |
4052 | + } |
4053 | + |
4054 | + QDateTime startTime = QDateTime::currentDateTime().toUTC(); |
4055 | + QScopedPointer<QNetworkAccessManager> networkAccessManager(new QNetworkAccessManager); |
4056 | + connect(networkAccessManager.data(), |
4057 | + SIGNAL(finished(QNetworkReply*)), |
4058 | + SLOT(onRequestFinished(QNetworkReply*)), |
4059 | + Qt::QueuedConnection); |
4060 | + |
4061 | + QEventLoop eventLoop; |
4062 | + mEventLoop = &eventLoop; |
4063 | + mUploadCompleted = false; |
4064 | + |
4065 | + while(!mQueue.isEmpty()) { |
4066 | + QPair<QString, QUrl> data = mQueue.takeFirst(); |
4067 | + |
4068 | + QByteArray imgData; |
4069 | + QFile imgFile(data.second.toLocalFile()); |
4070 | + if (!imgFile.open(QIODevice::ReadOnly)) { |
4071 | + LOG_WARNING("Fail to open file:" << data.second.toLocalFile()); |
4072 | + continue; |
4073 | + } |
4074 | + imgData = imgFile.readAll(); |
4075 | + imgFile.close();; |
4076 | + mCurrentRemoteId = data.first; |
4077 | + |
4078 | + // upload photo |
4079 | + QString requestUrl = QString(GOOGLE_PHOTO_URL) |
4080 | + .arg(mAccountId) |
4081 | + .arg(mCurrentRemoteId); |
4082 | + QNetworkRequest request(requestUrl); |
4083 | + request.setRawHeader(QStringLiteral("GData-Version").toUtf8(), QStringLiteral("3.0").toUtf8()); |
4084 | + request.setRawHeader(QStringLiteral("Authorization").toUtf8(), |
4085 | + QStringLiteral("Bearer %1").arg(mAuthToken).toUtf8()); |
4086 | + request.setRawHeader(QStringLiteral("Content-Type").toUtf8(), QStringLiteral("image/*").toUtf8()); |
4087 | + request.setRawHeader(QStringLiteral("If-Match").toUtf8(), QStringLiteral("*").toUtf8()); |
4088 | + networkAccessManager->put(request, imgData); |
4089 | + |
4090 | + // wait for the upload to finish |
4091 | + eventLoop.exec(); |
4092 | + |
4093 | + // should we abort? |
4094 | + if (mAbort) { |
4095 | + break; |
4096 | + } |
4097 | + } |
4098 | + |
4099 | + mUploadCompleted = true; |
4100 | + |
4101 | + if (mAbort) { |
4102 | + return; |
4103 | + } |
4104 | + |
4105 | + // After upload the pictures the contact etag get updated we need to retrieve |
4106 | + // the new ones |
4107 | + QString requestUrl = |
4108 | + QString("%1?updated-min=%2&fields=entry(@gd:etag, id, link(@rel, @gd:etag))") |
4109 | + .arg(GOOGLE_URL) |
4110 | + .arg(startTime.toString(Qt::ISODate)); |
4111 | + QNetworkRequest request(requestUrl); |
4112 | + request.setRawHeader(QStringLiteral("GData-Version").toUtf8(), QStringLiteral("3.0").toUtf8()); |
4113 | + request.setRawHeader(QStringLiteral("Authorization").toUtf8(), |
4114 | + QStringLiteral("Bearer %1").arg(mAuthToken).toUtf8()); |
4115 | + networkAccessManager->get(request); |
4116 | + |
4117 | + // wait for the reply to finish |
4118 | + eventLoop.exec(); |
4119 | +} |
4120 | + |
4121 | +void GContactImageUploader::onRequestFinished(QNetworkReply *reply) |
4122 | +{ |
4123 | + if (mUploadCompleted) { |
4124 | + if (reply->error() != QNetworkReply::NoError) { |
4125 | + LOG_WARNING("Fail to retrieve new etags:" << reply->errorString()); |
4126 | + } else { |
4127 | + QByteArray data = reply->readAll(); |
4128 | + LOG_TRACE("After avatar upload query result:" << data); |
4129 | + QMap<QString, GContactImageUploader::UploaderReply> entries = parseEntryList(data); |
4130 | + foreach(const QString &remoteId, entries.keys()) { |
4131 | + if (mResults.contains(remoteId)) { |
4132 | + mResults[remoteId] = entries[remoteId]; |
4133 | + emit uploadFinished(remoteId, entries[remoteId]); |
4134 | + } |
4135 | + } |
4136 | + } |
4137 | + } else { |
4138 | + mResults.insert(mCurrentRemoteId, UploaderReply()); |
4139 | + if (reply->error() != QNetworkReply::NoError) { |
4140 | + LOG_WARNING("Fail to upload avatar:" << reply->errorString()); |
4141 | + emit uploadError(mCurrentRemoteId, reply->errorString()); |
4142 | + } else { |
4143 | + LOG_TRACE("Avatar upload result" << reply->readAll()); |
4144 | + } |
4145 | + mCurrentRemoteId.clear(); |
4146 | + } |
4147 | + |
4148 | + if (mEventLoop) { |
4149 | + mEventLoop->quit(); |
4150 | + } |
4151 | +} |
4152 | + |
4153 | +QMap<QString, GContactImageUploader::UploaderReply> GContactImageUploader::parseEntryList(const QByteArray &data) const |
4154 | +{ |
4155 | + QMap<QString, UploaderReply> result; |
4156 | + |
4157 | + QDomDocument doc; |
4158 | + QString errorMsg; |
4159 | + if (!doc.setContent(data, &errorMsg)) { |
4160 | + LOG_WARNING("Fail to parse etag list" << errorMsg); |
4161 | + return result; |
4162 | + } |
4163 | + |
4164 | + QDomNodeList entries = doc.elementsByTagName("entry"); |
4165 | + for (int i = 0; i < entries.size(); i++) { |
4166 | + UploaderReply reply; |
4167 | + QDomElement entry = entries.item(i).toElement(); |
4168 | + QDomElement id = entry.firstChildElement("id"); |
4169 | + |
4170 | + reply.newEtag = entry.attribute("gd:etag"); |
4171 | + if (!id.isNull()) { |
4172 | + reply.remoteId = id.text().split('/').last(); |
4173 | + } |
4174 | + |
4175 | + QDomNodeList links = entry.elementsByTagName("link"); |
4176 | + for (int l = 0; l < links.size(); l++) { |
4177 | + QDomElement link = links.item(l).toElement(); |
4178 | + |
4179 | + if (link.attribute("rel") == "http://schemas.google.com/contacts/2008/rel#photo") { |
4180 | + reply.newAvatarEtag = link.attribute("gd:etag"); |
4181 | + break; |
4182 | + } |
4183 | + } |
4184 | + result.insert(reply.remoteId, reply); |
4185 | + } |
4186 | + |
4187 | + return result; |
4188 | +} |
4189 | |
4190 | === added file 'google/GContactImageUploader.h' |
4191 | --- google/GContactImageUploader.h 1970-01-01 00:00:00 +0000 |
4192 | +++ google/GContactImageUploader.h 2015-09-24 17:49:24 +0000 |
4193 | @@ -0,0 +1,78 @@ |
4194 | +/**************************************************************************** |
4195 | + ** |
4196 | + ** Copyright (C) 2015 Canonical Ltd. |
4197 | + ** |
4198 | + ** Contact: Renato Araujo Oliveira Filho <renato.filho@canonical.com> |
4199 | + ** |
4200 | + ** This program/library is free software; you can redistribute it and/or |
4201 | + ** modify it under the terms of the GNU Lesser General Public License |
4202 | + ** version 2.1 as published by the Free Software Foundation. |
4203 | + ** |
4204 | + ** This program/library is distributed in the hope that it will be useful, |
4205 | + ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
4206 | + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
4207 | + ** Lesser General Public License for more details. |
4208 | + ** |
4209 | + ** You should have received a copy of the GNU Lesser General Public |
4210 | + ** License along with this program/library; if not, write to the Free |
4211 | + ** Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
4212 | + ** 02110-1301 USA |
4213 | + ** |
4214 | + ****************************************************************************/ |
4215 | + |
4216 | +#ifndef GOOGLECONTACTIMAGEUPLOADER_H |
4217 | +#define GOOGLECONTACTIMAGEUPLOADER_H |
4218 | + |
4219 | +#include <QObject> |
4220 | +#include <QString> |
4221 | +#include <QUrl> |
4222 | +#include <QQueue> |
4223 | +#include <QMap> |
4224 | +#include <QMutex> |
4225 | +#include <QEventLoop> |
4226 | +#include <QNetworkReply> |
4227 | + |
4228 | + |
4229 | + |
4230 | +class GContactImageUploader: public QObject |
4231 | +{ |
4232 | + Q_OBJECT |
4233 | + |
4234 | +public: |
4235 | + class UploaderReply |
4236 | + { |
4237 | + public: |
4238 | + QString remoteId; |
4239 | + QString newEtag; |
4240 | + QString newAvatarEtag; |
4241 | + }; |
4242 | + |
4243 | + explicit GContactImageUploader(const QString &authToken, |
4244 | + const QString &accountId, |
4245 | + QObject *parent = 0); |
4246 | + |
4247 | + void push(const QString &remoteId, const QUrl &imgUrl); |
4248 | + QMap<QString, UploaderReply> result(); |
4249 | + void exec(); |
4250 | + |
4251 | +signals: |
4252 | + void uploadFinished(const QUrl &remoteId, const GContactImageUploader::UploaderReply &eTag); |
4253 | + void uploadError(const QUrl &remoteId, const QString &error); |
4254 | + |
4255 | +private slots: |
4256 | + void onRequestFinished(QNetworkReply *reply); |
4257 | + |
4258 | +private: |
4259 | + QEventLoop *mEventLoop; |
4260 | + QQueue<QPair<QString, QUrl> > mQueue; |
4261 | + QString mAuthToken; |
4262 | + QString mAccountId; |
4263 | + QString mCurrentRemoteId; |
4264 | + QMap<QString, UploaderReply> mResults; |
4265 | + bool mAbort; |
4266 | + bool mUploadCompleted; |
4267 | + |
4268 | + QMap<QString, UploaderReply> parseEntryList(const QByteArray &data) const; |
4269 | +}; |
4270 | + |
4271 | +#endif // GOOGLECONTACTIMAGEUPLOADER_H |
4272 | |
4273 | === added file 'google/GContactStream.cpp' |
4274 | --- google/GContactStream.cpp 1970-01-01 00:00:00 +0000 |
4275 | +++ google/GContactStream.cpp 2015-09-24 17:49:24 +0000 |
4276 | @@ -0,0 +1,1446 @@ |
4277 | +/**************************************************************************** |
4278 | + ** |
4279 | + ** Copyright (C) 2013-2014 Jolla Ltd. and/or its subsidiary(-ies). |
4280 | + ** 2015 Canonical Ltd. |
4281 | + ** |
4282 | + ** Contributors: Sateesh Kavuri <sateesh.kavuri@gmail.com> |
4283 | + ** Mani Chandrasekar <maninc@gmail.com> |
4284 | + ** Chris Adams <chris.adams@jolla.com> |
4285 | + ** Renato Araujo Oliveira Filho <renato.filho@canonical.com> |
4286 | + ** |
4287 | + ** This program/library is free software; you can redistribute it and/or |
4288 | + ** modify it under the terms of the GNU Lesser General Public License |
4289 | + ** version 2.1 as published by the Free Software Foundation. |
4290 | + ** |
4291 | + ** This program/library is distributed in the hope that it will be useful, |
4292 | + ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
4293 | + ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
4294 | + ** Lesser General Public License for more details. |
4295 | + ** |
4296 | + ** You should have received a copy of the GNU Lesser General Public |
4297 | + ** License along with this program/library; if not, write to the Free |
4298 | + ** Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
4299 | + ** 02110-1301 USA |
4300 | + ** |
4301 | + ****************************************************************************/ |
4302 | + |
4303 | +#include "GConfig.h" |
4304 | +#include "GContactStream.h" |
4305 | +#include "GContactAtom.h" |
4306 | +#include "UContactsCustomDetail.h" |
4307 | +#include "buteosyncfw_p.h" |
4308 | + |
4309 | +#include <QDateTime> |
4310 | +#include <QtContacts/QContactId> |
4311 | + |
4312 | +GoogleContactStream::GoogleContactStream(bool response, const QString &accountEmail, QObject* parent) |
4313 | + : QObject(parent) |
4314 | + , mXmlReader(0) |
4315 | + , mAtom(0) |
4316 | + , mXmlWriter(0) |
4317 | + , mAccountEmail(accountEmail) |
4318 | +{ |
4319 | + if (response == true) { |
4320 | + initResponseFunctionMap(); |
4321 | + } else { |
4322 | + initFunctionMap(); |
4323 | + } |
4324 | +} |
4325 | + |
4326 | +GoogleContactStream::~GoogleContactStream() |
4327 | +{ |
4328 | +} |
4329 | + |
4330 | +GoogleContactAtom *GoogleContactStream::parse(const QByteArray &xmlBuffer) |
4331 | +{ |
4332 | + mXmlReader = new QXmlStreamReader(xmlBuffer); |
4333 | + mAtom = new GoogleContactAtom; |
4334 | + |
4335 | + Q_CHECK_PTR(mXmlReader); |
4336 | + Q_CHECK_PTR(mAtom); |
4337 | + |
4338 | + while (!mXmlReader->atEnd() && !mXmlReader->hasError()) { |
4339 | + if (mXmlReader->readNextStartElement()) { |
4340 | + Handler handler = mAtomFunctionMap.value(mXmlReader->name().toString()); |
4341 | + if (handler) { |
4342 | + (*this.*handler)(); |
4343 | + } |
4344 | + } |
4345 | + } |
4346 | + |
4347 | + delete mXmlReader; |
4348 | + return mAtom; |
4349 | +} |
4350 | + |
4351 | +QByteArray GoogleContactStream::encode(const QMultiMap<GoogleContactStream::UpdateType, QPair<QContact, QStringList> > &updates) |
4352 | +{ |
4353 | + QByteArray xmlBuffer; |
4354 | + mXmlWriter = new QXmlStreamWriter(&xmlBuffer); |
4355 | + startBatchFeed(); |
4356 | + |
4357 | + QList<QPair<QContact, QStringList> > removedContacts = updates.values(GoogleContactStream::Remove); |
4358 | + for (int i = 0; i < removedContacts.size(); ++i) { |
4359 | + encodeContactUpdate(removedContacts[i].first, removedContacts[i].second, GoogleContactStream::Remove, true); // batchmode = true |
4360 | + } |
4361 | + |
4362 | + QList<QPair<QContact, QStringList> > addedContacts = updates.values(GoogleContactStream::Add); |
4363 | + for (int i = 0; i < addedContacts.size(); ++i) { |
4364 | + encodeContactUpdate(addedContacts[i].first, addedContacts[i].second, GoogleContactStream::Add, true); // batchmode = true |
4365 | + } |
4366 | + |
4367 | + QList<QPair<QContact, QStringList> > modifiedContacts = updates.values(GoogleContactStream::Modify); |
4368 | + for (int i = 0; i < modifiedContacts.size(); ++i) { |
4369 | + encodeContactUpdate(modifiedContacts[i].first, modifiedContacts[i].second, GoogleContactStream::Modify, true); // batchmode = true |
4370 | + } |
4371 | + |
4372 | + endBatchFeed(); |
4373 | + mXmlWriter->writeEndDocument(); |
4374 | + delete mXmlWriter; |
4375 | + return xmlBuffer; |
4376 | +} |
4377 | + |
4378 | +// ---------------------------------------- |
4379 | + |
4380 | +void GoogleContactStream::initAtomFunctionMap() |
4381 | +{ |
4382 | + mAtomFunctionMap.insert("updated", &GoogleContactStream::handleAtomUpdated); |
4383 | + mAtomFunctionMap.insert("category", &GoogleContactStream::handleAtomCategory); |
4384 | + mAtomFunctionMap.insert("author", &GoogleContactStream::handleAtomAuthor); |
4385 | + mAtomFunctionMap.insert("id", &GoogleContactStream::handleAtomId); |
4386 | + mAtomFunctionMap.insert("totalResults", &GoogleContactStream::handleAtomOpenSearch); |
4387 | + mAtomFunctionMap.insert("startIndex", &GoogleContactStream::handleAtomOpenSearch); |
4388 | + mAtomFunctionMap.insert("itemsPerPage", &GoogleContactStream::handleAtomOpenSearch); |
4389 | + mAtomFunctionMap.insert("link", &GoogleContactStream::handleAtomLink); |
4390 | + mAtomFunctionMap.insert("entry", &GoogleContactStream::handleAtomEntry); |
4391 | + mAtomFunctionMap.insert("generator", &GoogleContactStream::handleAtomGenerator); |
4392 | + mAtomFunctionMap.insert("title", &GoogleContactStream::handleAtomTitle); |
4393 | +} |
4394 | + |
4395 | +void GoogleContactStream::initResponseFunctionMap() |
4396 | +{ |
4397 | + initAtomFunctionMap(); |
4398 | + // TODO: move the batch request response handling stuff here. |
4399 | +} |
4400 | + |
4401 | +void GoogleContactStream::initFunctionMap() |
4402 | +{ |
4403 | + initAtomFunctionMap(); |
4404 | + mContactFunctionMap.insert("updated", &GoogleContactStream::handleEntryUpdated); |
4405 | + //mContactFunctionMap.insert("app:edited", &GoogleContactStream::handleEntryUpdated); |
4406 | + mContactFunctionMap.insert("gContact:birthday", &GoogleContactStream::handleEntryBirthday); |
4407 | + mContactFunctionMap.insert("gContact:gender", &GoogleContactStream::handleEntryGender); |
4408 | + mContactFunctionMap.insert("gContact:hobby", &GoogleContactStream::handleEntryHobby); |
4409 | + mContactFunctionMap.insert("gContact:nickname", &GoogleContactStream::handleEntryNickname); |
4410 | + mContactFunctionMap.insert("gContact:occupation", &GoogleContactStream::handleEntryOccupation); |
4411 | + mContactFunctionMap.insert("gContact:website", &GoogleContactStream::handleEntryWebsite); |
4412 | + mContactFunctionMap.insert("gContact:groupMembershipInfo", &GoogleContactStream::handleEntryGroup); |
4413 | + mContactFunctionMap.insert("gContact:event", &GoogleContactStream::handleEntryEvent); |
4414 | + mContactFunctionMap.insert("gContact:jot", &GoogleContactStream::handleEntryJot); |
4415 | + mContactFunctionMap.insert("gContact:relation", &GoogleContactStream::handleRelation); |
4416 | + mContactFunctionMap.insert("gd:email", &GoogleContactStream::handleEntryEmail); |
4417 | + mContactFunctionMap.insert("gd:im", &GoogleContactStream::handleEntryIm); |
4418 | + mContactFunctionMap.insert("gd:name", &GoogleContactStream::handleEntryName); |
4419 | + mContactFunctionMap.insert("gd:organization", &GoogleContactStream::handleEntryOrganization); |
4420 | + mContactFunctionMap.insert("gd:phoneNumber", &GoogleContactStream::handleEntryPhoneNumber); |
4421 | + mContactFunctionMap.insert("gd:structuredPostalAddress", &GoogleContactStream::handleEntryStructuredPostalAddress); |
4422 | + mContactFunctionMap.insert("gd:extendedProperty", &GoogleContactStream::handleEntryExtendedProperty); |
4423 | + |
4424 | +} |
4425 | + |
4426 | +// ---------------------------------------- |
4427 | + |
4428 | +void GoogleContactStream::handleAtomUpdated() |
4429 | +{ |
4430 | + Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->name() == "updated"); |
4431 | + mAtom->setUpdated(mXmlReader->readElementText()); |
4432 | +} |
4433 | + |
4434 | +void GoogleContactStream::handleAtomCategory() |
4435 | +{ |
4436 | + Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->name() == "category"); |
4437 | + |
4438 | + QXmlStreamAttributes attributes = mXmlReader->attributes(); |
4439 | + QString scheme, term; |
4440 | + if (attributes.hasAttribute("scheme")) { |
4441 | + scheme = attributes.value("scheme").toString(); |
4442 | + } |
4443 | + if (attributes.hasAttribute("term")) { |
4444 | + term = attributes.value("term").toString(); |
4445 | + } |
4446 | + |
4447 | + mAtom->setCategory(scheme, term); |
4448 | +} |
4449 | + |
4450 | +void GoogleContactStream::handleAtomAuthor() |
4451 | +{ |
4452 | + Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->name() == "author"); |
4453 | + |
4454 | + while (!(mXmlReader->tokenType() == QXmlStreamReader::EndElement && mXmlReader->name() == "author")) { |
4455 | + if (mXmlReader->tokenType() == QXmlStreamReader::StartElement) { |
4456 | + if (mXmlReader->name() == "name") { |
4457 | + mAtom->setAuthorName(mXmlReader->readElementText()); |
4458 | + } else if (mXmlReader->name() == "email") { |
4459 | + mAtom->setAuthorEmail(mXmlReader->readElementText()); |
4460 | + } |
4461 | + } |
4462 | + mXmlReader->readNextStartElement(); |
4463 | + } |
4464 | +} |
4465 | + |
4466 | +void GoogleContactStream::handleAtomOpenSearch() |
4467 | +{ |
4468 | + Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->prefix() == "openSearch"); |
4469 | + |
4470 | + if (mXmlReader->name() == "totalResults") { |
4471 | + mAtom->setTotalResults(mXmlReader->readElementText().toInt()); |
4472 | + } else if (mXmlReader->name() == "startIndex") { |
4473 | + mAtom->setStartIndex(mXmlReader->readElementText().toInt()); |
4474 | + } else if (mXmlReader->name() == "itemsPerPage") { |
4475 | + mAtom->setItemsPerPage(mXmlReader->readElementText().toInt()); |
4476 | + } |
4477 | +} |
4478 | + |
4479 | +void GoogleContactStream::handleAtomLink() |
4480 | +{ |
4481 | + Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->name() == "link"); |
4482 | + |
4483 | + if (mXmlReader->attributes().hasAttribute("rel") && (mXmlReader->attributes().value("rel") == "next")) { |
4484 | + mAtom->setNextEntriesUrl(mXmlReader->attributes().value("href").toString()); |
4485 | + } |
4486 | +} |
4487 | + |
4488 | +void GoogleContactStream::handleAtomId() |
4489 | +{ |
4490 | + Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->name() == "id"); |
4491 | + mAtom->setId(mXmlReader->readElementText()); |
4492 | +} |
4493 | + |
4494 | +void GoogleContactStream::handleAtomGenerator() |
4495 | +{ |
4496 | + Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->name() == "generator"); |
4497 | + |
4498 | + QXmlStreamAttributes attributes = mXmlReader->attributes(); |
4499 | + QString name, version, uri; |
4500 | + if (attributes.hasAttribute("version")) { |
4501 | + version = attributes.value("version").toString(); |
4502 | + } |
4503 | + if (attributes.hasAttribute("uri")) { |
4504 | + uri = attributes.value("uri").toString(); |
4505 | + } |
4506 | + name = mXmlReader->readElementText(); |
4507 | + |
4508 | + mAtom->setGenerator(name, version, uri); |
4509 | +} |
4510 | + |
4511 | +void GoogleContactStream::handleAtomTitle() |
4512 | +{ |
4513 | + Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->name() == "title"); |
4514 | + mAtom->setTitle(mXmlReader->readElementText()); |
4515 | +} |
4516 | + |
4517 | +void GoogleContactStream::handleAtomEntry() |
4518 | +{ |
4519 | + Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->name() == "entry"); |
4520 | + |
4521 | + // the entry will either be a contact, a group, or a response to a batch update request. |
4522 | + // if it's a group, we need to store some information about it. |
4523 | + QString systemGroupId; |
4524 | + QString systemGroupAtomId; |
4525 | + |
4526 | + // the entry will be a contact if this is a response to a "read" request |
4527 | + QContact entryContact; |
4528 | + QStringList unsupportedElements; |
4529 | + bool isInGroup = false; |
4530 | + bool isDeleted = false; |
4531 | + |
4532 | + // or it will be a series of batch operation success/fail info |
4533 | + // if this xml is the response to a batch update/delete request. |
4534 | + bool isBatchOperationResponse = false; |
4535 | + GoogleContactAtom::BatchOperationResponse response; |
4536 | + |
4537 | + while (!((mXmlReader->tokenType() == QXmlStreamReader::EndElement) && (mXmlReader->name() == "entry"))) { |
4538 | + if (mXmlReader->tokenType() == QXmlStreamReader::StartElement) { |
4539 | + isInGroup |= (mXmlReader->qualifiedName().toString() == QStringLiteral("gContact:groupMembershipInfo")); |
4540 | + DetailHandler handler = mContactFunctionMap.value(mXmlReader->qualifiedName().toString()); |
4541 | + if (handler) { |
4542 | + QContactDetail convertedDetail = (*this.*handler)(); |
4543 | + if (convertedDetail != QContactDetail()) { |
4544 | + entryContact.saveDetail(&convertedDetail); |
4545 | + } else { |
4546 | + LOG_WARNING("Handle not found for " << mXmlReader->qualifiedName().toString()); |
4547 | + } |
4548 | + } else if (mXmlReader->qualifiedName().toString() == QStringLiteral("gd:deleted")) { |
4549 | + isDeleted = true; |
4550 | + } else if (mXmlReader->qualifiedName().toString() == QStringLiteral("batch:id")) { |
4551 | + isBatchOperationResponse = true; |
4552 | + handleEntryBatchId(&response); |
4553 | + } else if (mXmlReader->qualifiedName().toString() == QStringLiteral("batch:operation")) { |
4554 | + isBatchOperationResponse = true; |
4555 | + handleEntryBatchOperation(&response); |
4556 | + } else if (mXmlReader->qualifiedName().toString() == QStringLiteral("batch:status")) { |
4557 | + isBatchOperationResponse = true; |
4558 | + handleEntryBatchStatus(&response); |
4559 | + } else if (mXmlReader->qualifiedName().toString() == QStringLiteral("link")) { |
4560 | + // There are several possible links: |
4561 | + // Avatar Photo link |
4562 | + // Self query link |
4563 | + // Edit link |
4564 | + // Batch link etc. |
4565 | + |
4566 | + // If it's an avatar, we grab it as a QContactAvatar detail |
4567 | + bool isAvatar = false; |
4568 | + QContactAvatar googleAvatar; |
4569 | + QString etag; |
4570 | + QString unsupportedElement = handleEntryLink(&googleAvatar, &isAvatar, &etag); |
4571 | + if (isAvatar) { |
4572 | + // check if we have already a google avatar |
4573 | + entryContact.saveDetail(&googleAvatar); |
4574 | + UContactsCustomDetail::setCustomField(entryContact, |
4575 | + UContactsCustomDetail::FieldContactAvatarETag, |
4576 | + etag); |
4577 | + } |
4578 | + |
4579 | + // Whether it's an avatar or not, we also store the element text. |
4580 | + if (!unsupportedElement.isEmpty()) { |
4581 | + unsupportedElements.append(unsupportedElement); |
4582 | + } |
4583 | + } else if (mXmlReader->name().toString() == QStringLiteral("entry")) { |
4584 | + // read the etag out of the entry. |
4585 | + response.eTag = mXmlReader->attributes().value("gd:etag").toString(); |
4586 | + } else if (mXmlReader->qualifiedName().toString() == QStringLiteral("gContact:systemGroup")) { |
4587 | + systemGroupId = mXmlReader->attributes().value("id").toString(); |
4588 | + } else if (mXmlReader->qualifiedName().toString() == QStringLiteral("id")) { |
4589 | + // either a contact id or a group id. |
4590 | + QContactDetail guidDetail = handleEntryId(&systemGroupAtomId); |
4591 | + entryContact.saveDetail(&guidDetail); |
4592 | + } else { |
4593 | + // This is some XML element which we don't handle. |
4594 | + // We should store it, so that we can send it back when we upload changes. |
4595 | + QString unsupportedElement = handleEntryUnknownElement(); |
4596 | + if (!unsupportedElement.isEmpty()) { |
4597 | + unsupportedElements.append(unsupportedElement); |
4598 | + } |
4599 | + } |
4600 | + } |
4601 | + mXmlReader->readNextStartElement(); |
4602 | + } |
4603 | + |
4604 | + if (!systemGroupId.isEmpty()) { |
4605 | + // this entry was a group |
4606 | + mAtom->addEntrySystemGroup(systemGroupId, systemGroupAtomId); |
4607 | + } else { |
4608 | + // this entry was a contact. |
4609 | + // the etag is the "version identifier". Save it into the QCOM detail. |
4610 | + if (!response.eTag.isEmpty()) { |
4611 | + QtContacts::QContactExtendedDetail etagDetail = |
4612 | + UContactsCustomDetail::getCustomField(entryContact, UContactsCustomDetail::FieldContactETag); |
4613 | + etagDetail.setData(response.eTag); |
4614 | + entryContact.saveDetail(&etagDetail); |
4615 | + } |
4616 | + |
4617 | + if (isInGroup) { |
4618 | + // Only sync the contact if it is in a "real" group |
4619 | + // as otherwise we get hundreds of "Other Contacts" |
4620 | + // (random email addresses etc). |
4621 | + if (isDeleted) { |
4622 | + // if contact is deleted set the deletedAt value |
4623 | + QContactExtendedDetail deletedAt = |
4624 | + UContactsCustomDetail::getCustomField(entryContact, UContactsCustomDetail::FieldDeletedAt); |
4625 | + deletedAt.setData(QDateTime::currentDateTime()); |
4626 | + entryContact.saveDetail(&deletedAt); |
4627 | + |
4628 | + mAtom->addDeletedEntryContact(entryContact); |
4629 | + } else { |
4630 | + mAtom->addEntryContact(entryContact, unsupportedElements); |
4631 | + } |
4632 | + } |
4633 | + } |
4634 | + |
4635 | + if (isBatchOperationResponse) { |
4636 | + if (!entryContact.detail<QContactGuid>().guid().isEmpty()) { |
4637 | + response.contactGuid = entryContact.detail<QContactGuid>().guid(); |
4638 | + } |
4639 | + mAtom->addBatchOperationResponse(response.operationId, response); |
4640 | + } |
4641 | +} |
4642 | + |
4643 | +QString GoogleContactStream::handleEntryLink(QContactAvatar *avatar, |
4644 | + bool *isAvatar, |
4645 | + QString *etag) |
4646 | +{ |
4647 | + Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->name() == "link"); |
4648 | + QXmlStreamAttributes attributes = mXmlReader->attributes(); |
4649 | + |
4650 | + // if a contact does not have a photo, then the photo link element has no gd:etag attribute. |
4651 | + *isAvatar = ((attributes.value("rel") == "http://schemas.google.com/contacts/2008/rel#photo") && |
4652 | + attributes.hasAttribute("gd:etag")); |
4653 | + |
4654 | + if (*isAvatar) { |
4655 | + // this is an avatar photo for the contact entry |
4656 | + avatar->setImageUrl(attributes.value("href").toString()); |
4657 | + *etag = attributes.value("gd:etag").toString(); |
4658 | + } |
4659 | + |
4660 | + return handleEntryUnknownElement(); |
4661 | +} |
4662 | + |
4663 | +QContactDetail GoogleContactStream::handleEntryExtendedProperty() |
4664 | +{ |
4665 | + Q_ASSERT(mXmlReader->isStartElement() && |
4666 | + mXmlReader->name() == "gd:extendedProperty"); |
4667 | + |
4668 | + QContactExtendedDetail xd; |
4669 | + QXmlStreamAttributes attributes = mXmlReader->attributes(); |
4670 | + QString propName = attributes.value("name").toString(); |
4671 | + |
4672 | + // FIXME: |
4673 | + // We use extendedProperty to store favorite property until we implment support |
4674 | + // for google groups sync |
4675 | + if (propName == "X-FAVORITE") { |
4676 | + QContactFavorite fav; |
4677 | + fav.setFavorite(attributes.value("value").toString() == "true"); |
4678 | + return fav; |
4679 | + } else if (propName == "SOUND") { |
4680 | + QContactRingtone ring; |
4681 | + ring.setAudioRingtoneUrl(QUrl(attributes.value("value").toString())); |
4682 | + return ring; |
4683 | + } |
4684 | + |
4685 | + // handle as generic type |
4686 | + xd.setName(attributes.value("name").toString()); |
4687 | + xd.setData(attributes.value("value").toString()); |
4688 | + return xd; |
4689 | +} |
4690 | + |
4691 | +QContactDetail GoogleContactStream::handleEntryEvent() |
4692 | +{ |
4693 | + Q_ASSERT(mXmlReader->isStartElement() && |
4694 | + mXmlReader->name() == "gContact:event"); |
4695 | + |
4696 | +// <gContact:event rel="anniversary" label="memorial"> |
4697 | +// <gd:when startTime="2005-06-06" endTime="2005-06-08" valueString="This month"/> |
4698 | +// </gContact:event> |
4699 | + static QMap<QString, QContactAnniversary::SubType> anniversaryTypes; |
4700 | + if (anniversaryTypes.isEmpty()) { |
4701 | + anniversaryTypes.insert(QString::fromLatin1("engagement"), |
4702 | + QContactAnniversary::SubTypeEngagement); |
4703 | + anniversaryTypes.insert(QString::fromLatin1("employment"), |
4704 | + QContactAnniversary::SubTypeEmployment); |
4705 | + anniversaryTypes.insert(QString::fromLatin1("memorial"), |
4706 | + QContactAnniversary::SubTypeMemorial); |
4707 | + anniversaryTypes.insert(QString::fromLatin1("house"), |
4708 | + QContactAnniversary::SubTypeHouse); |
4709 | + anniversaryTypes.insert(QString::fromLatin1("wedding"), |
4710 | + QContactAnniversary::SubTypeWedding); |
4711 | + } |
4712 | + QXmlStreamAttributes attributes = mXmlReader->attributes(); |
4713 | + if (attributes.value("rel") == "anniversary") { |
4714 | + QContactAnniversary anniversary; |
4715 | + anniversary.setSubType(anniversaryTypes.value(attributes.value("label").toString(), |
4716 | + QContactAnniversary::SubTypeWedding)); |
4717 | + mXmlReader->readNextStartElement(); |
4718 | + if (mXmlReader->qualifiedName() == "gd:when") { |
4719 | + attributes = mXmlReader->attributes(); |
4720 | + anniversary.setOriginalDateTime(QDateTime::fromString(attributes.value("startTime").toString(), |
4721 | + Qt::ISODate)); |
4722 | + anniversary.setEvent(attributes.value("valueString").toString()); |
4723 | + // FIXME: missing endTime |
4724 | + // QContactAnniversary API does not support endTime |
4725 | + } |
4726 | + return anniversary; |
4727 | + } |
4728 | + |
4729 | + return QContactDetail(); |
4730 | +} |
4731 | + |
4732 | +QContactDetail GoogleContactStream::handleRelation() |
4733 | +{ |
4734 | + Q_ASSERT(mXmlReader->isStartElement() && |
4735 | + mXmlReader->name() == "gContact:relation"); |
4736 | + |
4737 | + QXmlStreamAttributes attributes = mXmlReader->attributes(); |
4738 | + QString rel = attributes.hasAttribute("rel") |
4739 | + ? attributes.value("rel").toString() |
4740 | + : QString(); |
4741 | + |
4742 | + QContactFamily family; |
4743 | + if (rel == "spouse") { |
4744 | + family.setSpouse(mXmlReader->readElementText()); |
4745 | + } else if (rel == "child") { |
4746 | + family.setChildren(QStringList() << mXmlReader->readElementText()); |
4747 | + } else { |
4748 | + LOG_WARNING("Family relation type not supported" << rel); |
4749 | + return QContactDetail(); |
4750 | + } |
4751 | + |
4752 | + return family; |
4753 | +} |
4754 | + |
4755 | +QString GoogleContactStream::handleEntryUnknownElement() |
4756 | +{ |
4757 | + Q_ASSERT(mXmlReader->isStartElement()); |
4758 | + |
4759 | + QXmlStreamAttributes attributes = mXmlReader->attributes(); |
4760 | + QString attributesString; |
4761 | + for (int i = 0; i < attributes.size(); ++i) { |
4762 | + QString extra = QStringLiteral(" %1=\"%2\"") |
4763 | + .arg(attributes[i].qualifiedName().toString()) |
4764 | + .arg(attributes[i].value().toString().toHtmlEscaped()); |
4765 | + attributesString.append(extra); |
4766 | + } |
4767 | + |
4768 | + QString unknownElement = QStringLiteral("<%1%2>%3</%1>") |
4769 | + .arg(mXmlReader->qualifiedName().toString()) |
4770 | + .arg(attributesString) |
4771 | + .arg(mXmlReader->text().toString()); |
4772 | + |
4773 | + return unknownElement; |
4774 | +} |
4775 | + |
4776 | +QList<int> GoogleContactStream::handleContext(const QString &rel) const |
4777 | +{ |
4778 | + QList<int> contexts; |
4779 | + QString name = rel.split("#").last(); |
4780 | + if (name == QStringLiteral("home")) { |
4781 | + contexts << QContactDetail::ContextHome; |
4782 | + } else if (name == QStringLiteral("work")) { |
4783 | + contexts << QContactDetail::ContextWork; |
4784 | + } else if (!name.isEmpty()) { |
4785 | + contexts << QContactDetail::ContextOther; |
4786 | + } |
4787 | + return contexts; |
4788 | +} |
4789 | + |
4790 | +void GoogleContactStream::handleEntryBatchStatus(GoogleContactAtom::BatchOperationResponse *response) |
4791 | +{ |
4792 | + Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->name() == "status"); |
4793 | + |
4794 | + response->code = mXmlReader->attributes().value("code").toString(); |
4795 | + response->reason = mXmlReader->attributes().value("reason").toString(); |
4796 | + response->reasonDescription = mXmlReader->readElementText(); |
4797 | + response->isError = true; |
4798 | + if (response->code == QStringLiteral("200") // No error. |
4799 | + || response->code == QStringLiteral("201") // Created without error. |
4800 | + || response->code == QStringLiteral("304")) { // Not modified (no change since time specified) |
4801 | + // according to Google Data API these response codes signify success cases. |
4802 | + response->isError = false; |
4803 | + } |
4804 | +} |
4805 | + |
4806 | +void GoogleContactStream::handleEntryBatchOperation(GoogleContactAtom::BatchOperationResponse *response) |
4807 | +{ |
4808 | + Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->name() == "operation"); |
4809 | + response->type = mXmlReader->attributes().value("type").toString(); |
4810 | +} |
4811 | + |
4812 | +void GoogleContactStream::handleEntryBatchId(GoogleContactAtom::BatchOperationResponse *response) |
4813 | +{ |
4814 | + Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->name() == "id"); |
4815 | + response->operationId = mXmlReader->readElementText(); |
4816 | +} |
4817 | + |
4818 | +QContactDetail GoogleContactStream::handleEntryId(QString *rawId) |
4819 | +{ |
4820 | + *rawId = mXmlReader->readElementText(); |
4821 | + QString idUrl = *rawId; |
4822 | + QContactGuid guid; |
4823 | + guid.setGuid(idUrl.split('/').last()); |
4824 | + return guid; |
4825 | +} |
4826 | + |
4827 | +QContactDetail GoogleContactStream::handleEntryBirthday() |
4828 | +{ |
4829 | + Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->qualifiedName() == "gContact:birthday"); |
4830 | + |
4831 | + QContactBirthday birthday; |
4832 | + birthday.setDate(QDate::fromString(mXmlReader->attributes().value("when").toString(), Qt::ISODate)); |
4833 | + |
4834 | + if (birthday.dateTime().isValid()) { |
4835 | + return birthday; |
4836 | + } else { |
4837 | + LOG_WARNING("Birthday date not supported:" << mXmlReader->attributes().value("when").toString()); |
4838 | + return QContactDetail(); |
4839 | + } |
4840 | +} |
4841 | + |
4842 | +QContactDetail GoogleContactStream::handleEntryGender() |
4843 | +{ |
4844 | + Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->qualifiedName() == "gContact:gender"); |
4845 | + |
4846 | + QString genderStr = mXmlReader->attributes().value("value").toString().toLower(); |
4847 | + QContactGender gender; |
4848 | + if (genderStr.startsWith('m')) { |
4849 | + gender.setGender(QContactGender::GenderMale); |
4850 | + } else if (genderStr.startsWith('f')) { |
4851 | + gender.setGender(QContactGender::GenderFemale); |
4852 | + } else { |
4853 | + gender.setGender(QContactGender::GenderUnspecified); |
4854 | + } |
4855 | + |
4856 | + return gender; |
4857 | +} |
4858 | + |
4859 | +QContactDetail GoogleContactStream::handleEntryHobby() |
4860 | +{ |
4861 | + Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->qualifiedName() == "gContact:hobby"); |
4862 | + |
4863 | + QContactHobby hobby; |
4864 | + hobby.setHobby(mXmlReader->readElementText()); |
4865 | + return hobby; |
4866 | +} |
4867 | + |
4868 | +QContactDetail GoogleContactStream::handleEntryNickname() |
4869 | +{ |
4870 | + Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->qualifiedName() == "gContact:nickname"); |
4871 | + |
4872 | + QContactNickname nickname; |
4873 | + nickname.setNickname(mXmlReader->readElementText()); |
4874 | + return nickname; |
4875 | +} |
4876 | + |
4877 | +QContactDetail GoogleContactStream::handleEntryOccupation() |
4878 | +{ |
4879 | + Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->qualifiedName() == "gContact:occupation"); |
4880 | + |
4881 | + QContactOrganization org; |
4882 | + org.setRole(mXmlReader->readElementText()); |
4883 | + return org; |
4884 | +} |
4885 | + |
4886 | +QContactDetail GoogleContactStream::handleEntryWebsite() |
4887 | +{ |
4888 | + Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->qualifiedName() == "gContact:website"); |
4889 | + |
4890 | + QContactUrl url; |
4891 | + QXmlStreamAttributes attributes = mXmlReader->attributes(); |
4892 | + QString rel = attributes.hasAttribute("rel") |
4893 | + ? attributes.value("rel").toString() |
4894 | + : QString(); |
4895 | + |
4896 | + if (rel == "home-page") { |
4897 | + url.setSubType(QContactUrl::SubTypeHomePage); |
4898 | + } else if (rel == "blog") { |
4899 | + url.setSubType(QContactUrl::SubTypeBlog); |
4900 | + } else { |
4901 | + url.setSubType(QContactUrl::SubTypeFavourite); |
4902 | + } |
4903 | + url.setContexts(handleContext(rel)); |
4904 | + url.setUrl(attributes.value("href").toString()); |
4905 | + return url; |
4906 | +} |
4907 | + |
4908 | +QContactDetail GoogleContactStream::handleEntryJot() |
4909 | +{ |
4910 | + Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->qualifiedName() == "gContact:jot"); |
4911 | + |
4912 | + QString rel = mXmlReader->attributes().hasAttribute("rel") |
4913 | + ? mXmlReader->attributes().value("rel").toString() |
4914 | + : QString(); |
4915 | + |
4916 | + QContactNote note; |
4917 | + note.setContexts(handleContext(rel)); |
4918 | + note.setNote(mXmlReader->readElementText()); |
4919 | + return note; |
4920 | +} |
4921 | + |
4922 | +QContactDetail GoogleContactStream::handleEntryEmail() |
4923 | +{ |
4924 | + Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->qualifiedName() == "gd:email"); |
4925 | + |
4926 | + QContactEmailAddress email; |
4927 | + email.setEmailAddress(mXmlReader->attributes().value("address").toString()); |
4928 | + |
4929 | + QString rel = mXmlReader->attributes().hasAttribute("rel") |
4930 | + ? mXmlReader->attributes().value("rel").toString() |
4931 | + : QString(); |
4932 | + email.setContexts(handleContext(rel)); |
4933 | + return email; |
4934 | +} |
4935 | + |
4936 | +QContactDetail GoogleContactStream::handleEntryIm() |
4937 | +{ |
4938 | + Q_ASSERT(mXmlReader->isStartElement () && mXmlReader->qualifiedName () == "gd:im"); |
4939 | + |
4940 | + static QMap<QString, QContactOnlineAccount::Protocol> protocolMap; |
4941 | + if (protocolMap.isEmpty()) { |
4942 | + protocolMap.insert("AIM", QContactOnlineAccount::ProtocolAim); |
4943 | + protocolMap.insert("MSN", QContactOnlineAccount::ProtocolMsn); |
4944 | + protocolMap.insert("YAHOO", QContactOnlineAccount::ProtocolYahoo); |
4945 | + protocolMap.insert("SKYPE", QContactOnlineAccount::ProtocolSkype); |
4946 | + protocolMap.insert("ICQ", QContactOnlineAccount::ProtocolIcq); |
4947 | + protocolMap.insert("JABBER", QContactOnlineAccount::ProtocolJabber); |
4948 | + protocolMap.insert("QQ", QContactOnlineAccount::ProtocolQq); |
4949 | + protocolMap.insert("IRC", QContactOnlineAccount::ProtocolIrc); |
4950 | + } |
4951 | + QString rel, protocol; |
4952 | + if (mXmlReader->attributes().hasAttribute("rel")) { |
4953 | + rel = mXmlReader->attributes().value("rel").toString(); |
4954 | + } |
4955 | + |
4956 | + if (mXmlReader->attributes().hasAttribute ("protocol")) { |
4957 | + QString protocolUrl = mXmlReader->attributes().value("protocol").toString(); |
4958 | + protocol = protocolUrl.split("#").last(); |
4959 | + } |
4960 | + |
4961 | + QContactOnlineAccount imAccount; |
4962 | + imAccount.setAccountUri(mXmlReader->attributes().value("address").toString()); |
4963 | + imAccount.setProtocol(protocolMap.value(protocol, QContactOnlineAccount::ProtocolUnknown)); |
4964 | + if (!protocolMap.contains(protocol)) { |
4965 | + imAccount.setServiceProvider(protocol); |
4966 | + } |
4967 | + imAccount.setContexts(handleContext(rel)); |
4968 | + return imAccount; |
4969 | +} |
4970 | + |
4971 | +QContactDetail GoogleContactStream::handleEntryName() |
4972 | +{ |
4973 | + Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->qualifiedName() == "gd:name"); |
4974 | + |
4975 | + QContactName name; |
4976 | + while (!(mXmlReader->tokenType() == QXmlStreamReader::EndElement && mXmlReader->qualifiedName() == "gd:name")) { |
4977 | + if (mXmlReader->tokenType() == QXmlStreamReader::StartElement) { |
4978 | + if (mXmlReader->qualifiedName() == "gd:givenName") { |
4979 | + name.setFirstName(mXmlReader->readElementText()); |
4980 | + } else if (mXmlReader->qualifiedName() == "gd:additionalName") { |
4981 | + name.setMiddleName(mXmlReader->readElementText()); |
4982 | + } else if (mXmlReader->qualifiedName() == "gd:familyName") { |
4983 | + name.setLastName(mXmlReader->readElementText()); |
4984 | + } else if (mXmlReader->qualifiedName() == "gd:namePrefix") { |
4985 | + name.setPrefix(mXmlReader->readElementText()); |
4986 | + } else if (mXmlReader->qualifiedName() == "gd:nameSuffix") { |
4987 | + name.setSuffix(mXmlReader->readElementText()); |
4988 | + } |
4989 | + } |
4990 | + mXmlReader->readNextStartElement(); |
4991 | + } |
4992 | + |
4993 | + return name; |
4994 | +} |
4995 | + |
4996 | +QContactDetail GoogleContactStream::handleEntryOrganization() |
4997 | +{ |
4998 | + Q_ASSERT(mXmlReader->isStartElement() && mXmlReader->qualifiedName() == "gd:organization"); |
4999 | + |
5000 | + QContactOrganization org; |
FAILED: Continuous integration, rev:13 jenkins. qa.ubuntu. com/job/ buteo-sync- plugins- contacts- ci/3/ jenkins. qa.ubuntu. com/job/ buteo-sync- plugins- contacts- vivid-amd64- ci/3/console jenkins. qa.ubuntu. com/job/ buteo-sync- plugins- contacts- vivid-armhf- ci/3/console jenkins. qa.ubuntu. com/job/ buteo-sync- plugins- contacts- vivid-i386- ci/3/console
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/buteo- sync-plugins- contacts- ci/3/rebuild
http://