37
37
#include <akonadi/contact/contacteditor.h>
38
38
#include <akonadi/contact/contacteditordialog.h>
39
39
#include <akonadi/session.h>
40
#include <akonadi/monitor.h>
40
41
#include <kabc/addressee.h>
41
42
#include <kabc/addresseelist.h>
42
43
#include <kabc/contactgroup.h>
43
#include <kabc/phonenumber.h>
46
46
#include "../lib/contact.h"
47
#include "../lib/accountlist.h"
47
#include "../lib/accountlistmodel.h"
48
48
#include "../lib/account.h"
49
#include "configurationskeleton.h"
49
#include "../lib/call.h"
50
#include "../lib/callmodel.h"
51
#include "../lib/phonenumber.h"
52
#include "../lib/phonedirectorymodel.h"
53
#include "../lib/numbercategorymodel.h"
54
#include "../lib/numbercategory.h"
55
#include "kcfg_settings.h"
51
57
///Init static attributes
52
58
AkonadiBackend* AkonadiBackend::m_pInstance = nullptr;
55
AkonadiBackend::AkonadiBackend(QObject* parent) : ContactBackend(parent)
61
AkonadiBackend::AkonadiBackend(QObject* parent) : AbstractContactBackend(parent)
57
63
m_pSession = new Akonadi::Session( "SFLPhone::instance" );
59
// fetching all collections containing emails recursively, starting at the root collection
60
Akonadi::CollectionFetchJob *job = new Akonadi::CollectionFetchJob( Akonadi::Collection::root(), Akonadi::CollectionFetchJob::Recursive, this );
61
job->fetchScope().setContentMimeTypes( QStringList() << "text/directory" );
62
connect( job, SIGNAL(collectionsReceived(Akonadi::Collection::List)), this, SLOT(collectionsReceived(Akonadi::Collection::List)) );
65
// fetching all collections recursively, starting at the root collection
66
m_pJob = new Akonadi::CollectionFetchJob( Akonadi::Collection::root(), Akonadi::CollectionFetchJob::Recursive, this );
67
m_pJob->fetchScope().setContentMimeTypes( QStringList() << "text/directory" );
68
connect( m_pJob, SIGNAL(collectionsReceived(Akonadi::Collection::List)), this, SLOT(collectionsReceived(Akonadi::Collection::List)) );
70
//Configure change monitor
71
m_pMonitor = new Akonadi::Monitor(this);
72
m_pMonitor->fetchCollectionStatistics(false);
73
Akonadi::ItemFetchScope scope;
74
scope.fetchFullPayload(true);
75
m_pMonitor->setItemFetchScope(scope);
76
connect(m_pMonitor,SIGNAL(itemAdded(Akonadi::Item,Akonadi::Collection)),
77
this,SLOT(slotItemAdded(Akonadi::Item,Akonadi::Collection)));
78
connect(m_pMonitor,SIGNAL(itemChanged(const Akonadi::Item,const QSet<QByteArray>)),
79
this,SLOT(slotItemChanged(const Akonadi::Item,const QSet<QByteArray>)));
80
connect(m_pMonitor,SIGNAL(itemRemoved(const Akonadi::Item)),
81
this,SLOT(slotItemRemoved(const Akonadi::Item)));
66
85
AkonadiBackend::~AkonadiBackend()
68
CallModel<>::destroy();
88
if (Call::contactBackend() == this)
89
Call::setContactBackend(nullptr);
85
107
return m_pInstance;
88
///Find contact using a phone number
89
///@param resolveDNS check if the DNS is used by an account, then assume contact with that phone number / extension is the same as the caller
90
Contact* AkonadiBackend::getContactByPhone(const QString& phoneNumber,bool resolveDNS,Account* a)
92
//Remove protocol dependant prefix and suffix
93
QString number = phoneNumber;
94
if (number.left(5) == "<sip:")
95
number = number.remove(0,5);
96
if (number.right(1) == ">")
97
number = number.remove(number.size()-1,1);
100
Contact* c = m_ContactByPhone[number];
105
a = AccountList::getInstance()->getDefaultAccount();
106
else if (number.indexOf('@') == -1 && a)
107
return m_ContactByPhone[number+'@'+a->getAccountHostname()];
109
//Use default resolve account to trim hostname away from the number
110
Contact* userOnly = m_ContactByPhone[getUserFromPhone(number).trimmed()];
111
QString defaultResolveAccount = ConfigurationSkeleton::defaultAccountId();
112
if (resolveDNS && !defaultResolveAccount.isEmpty() && number.indexOf('@') != -1) {
113
Account* defResolveAcc = AccountList::getInstance()->getAccountById(defaultResolveAccount);
114
QString hostname = defResolveAcc?defResolveAcc->getAccountHostname():QString();
115
if (defResolveAcc && hostname == number.right(hostname.size())) {
120
//Try to find something matching, but at this point it is not 100% sure it is the right one
121
if (resolveDNS && number.indexOf('@') != -1 && !getHostNameFromPhone(number).isEmpty() && userOnly) {
122
foreach (Account* a, AccountList::getInstance()->getAccounts()) {
123
if (a->getAccountHostname() == getHostNameFromPhone(number) && userOnly)
130
} //getContactByPhone
132
110
///Find contact by UID
133
111
Contact* AkonadiBackend::getContactByUid(const QString& uid)
135
113
return m_ContactByUid[uid];
116
///Return contact list
117
const ContactList& AkonadiBackend::getContactList() const
139
123
/*****************************************************************************
161
146
return KABC::PhoneNumber::Home;
149
void AkonadiBackend::fillContact(Contact* c, const KABC::Addressee& addr) const
151
c->setNickName (addr.nickName() );
152
c->setFormattedName (addr.formattedName() );
153
c->setFirstName (addr.givenName() );
154
c->setFamilyName (addr.familyName() );
155
c->setOrganization (addr.organization() );
156
c->setPreferredEmail (addr.preferredEmail() );
157
c->setDepartment (addr.department() );
158
c->setUid (addr.uid() );
160
const KABC::PhoneNumber::List numbers = addr.phoneNumbers();
161
Contact::PhoneNumbers newNumbers(c);
162
foreach (const KABC::PhoneNumber& number, numbers) {
163
newNumbers << PhoneDirectoryModel::instance()->getNumber(number.number(),c,nullptr,number.typeLabel());
164
QString number2 = number.number();
165
if (number2.left (5) == "<sip:")
166
number2 = number2.remove(0,5);
167
if (number2.right(1) == ">" )
168
number2 = number2.remove(number2.size()-2,1);
170
c->setPhoneNumbers (newNumbers );
173
Contact* AkonadiBackend::addItem(Akonadi::Item item, bool ignoreEmpty)
175
Contact* aContact = nullptr;
176
if ( item.hasPayload<KABC::Addressee>() ) {
177
m_pMonitor->setItemMonitored(item,true);
178
KABC::Addressee tmp = item.payload<KABC::Addressee>();
179
const KABC::PhoneNumber::List numbers = tmp.phoneNumbers();
180
const QString uid = tmp.uid();
182
if (numbers.size() || !ignoreEmpty) {
183
aContact = new Contact(this);
185
//This need to be done first because of the phone numbers indexes
186
fillContact(aContact,tmp);
188
m_ContactByUid[uid] = aContact;
190
if (!tmp.photo().data().isNull())
191
aContact->setPhoto(new QPixmap(QPixmap::fromImage( tmp.photo().data()).scaled(QSize(48,48))));
193
aContact->setPhoto(0);
195
m_AddrHash[ uid ] = tmp ;
196
m_ItemHash[ uid ] = item;
165
203
/*****************************************************************************
171
209
///Update the contact list when a new Akonadi collection is added
172
210
ContactList AkonadiBackend::update(Akonadi::Collection collection)
174
Account* defaultAccount = AccountList::getInstance()->getDefaultAccount();
175
m_Collection = collection;
176
212
if ( !collection.isValid() ) {
177
213
kDebug() << "The current collection is not valid";
178
214
return ContactList();
217
const bool onlyWithNumber = ConfigurationSkeleton::hideContactWithoutPhone();
181
219
Akonadi::RecursiveItemFetchJob *job = new Akonadi::RecursiveItemFetchJob( collection, QStringList() << KABC::Addressee::mimeType() << KABC::ContactGroup::mimeType());
182
220
job->fetchScope().fetchFullPayload();
183
221
if ( job->exec() ) {
185
222
const Akonadi::Item::List items = job->items();
187
224
foreach ( const Akonadi::Item &item, items ) {
188
if ( item.hasPayload<KABC::ContactGroup>() ) {
189
kDebug() << "Group:" << item.payload<KABC::ContactGroup>().name();
192
if ( item.hasPayload<KABC::Addressee>() ) {
193
KABC::Addressee tmp = item.payload<KABC::Addressee>();
194
Contact* aContact = new Contact();
196
const KABC::PhoneNumber::List numbers = tmp.phoneNumbers();
197
PhoneNumbers newNumbers;
198
foreach (const KABC::PhoneNumber& number, numbers) {
199
newNumbers << new Contact::PhoneNumber(number.number(),number.typeLabel());
200
QString number2 = number.number();
201
if (number2.left (5) == "<sip:")
202
number2 = number2.remove(0,5);
203
if (number2.right(1) == ">" )
204
number2 = number2.remove(number2.size()-1,1);
206
m_ContactByPhone[number2] = aContact;
208
if (number2.size() <= 6 && defaultAccount && !defaultAccount->getAccountHostname().isEmpty())
209
m_ContactByPhone[number2+'@'+defaultAccount->getAccountHostname()] = aContact;
211
m_ContactByUid[tmp.uid()] = aContact;
213
aContact->setNickName (tmp.nickName() );
214
aContact->setFormattedName (tmp.formattedName() );
215
aContact->setFirstName (tmp.givenName() );
216
aContact->setFamilyName (tmp.familyName() );
217
aContact->setOrganization (tmp.organization() );
218
aContact->setPreferredEmail (tmp.preferredEmail() );
219
aContact->setDepartment (tmp.department() );
220
aContact->setUid (tmp.uid() );
221
aContact->setPhoneNumbers (newNumbers );
223
if (!tmp.photo().data().isNull())
224
aContact->setPhoto(new QPixmap(QPixmap::fromImage( tmp.photo().data()).scaled(QSize(48,48))));
226
aContact->setPhoto(0);
228
m_AddrHash[ tmp.uid() ] = tmp ;
229
m_ItemHash[ tmp.uid() ] = item;
225
addItem(item,onlyWithNumber);
232
228
m_pContacts = m_ContactByUid.values();
234
231
return m_ContactByUid.values();
262
261
void AkonadiBackend::addNewContact(Contact* contact,QWidget* parent)
264
263
KABC::Addressee newContact;
265
newContact.setNickName ( contact->getNickName() );
266
newContact.setFormattedName ( contact->getFormattedName() );
267
newContact.setGivenName ( contact->getFirstName() );
268
newContact.setFamilyName ( contact->getSecondName() );
269
newContact.setOrganization ( contact->getOrganization() );
270
newContact.setDepartment ( contact->getDepartment() );
264
newContact.setNickName ( contact->nickName() );
265
newContact.setFormattedName ( contact->formattedName() );
266
newContact.setGivenName ( contact->firstName() );
267
newContact.setFamilyName ( contact->secondName() );
268
newContact.setOrganization ( contact->organization() );
269
newContact.setDepartment ( contact->department() );
271
270
//newContact.setPreferredEmail ( contact->getPreferredEmail() );//TODO
273
foreach (Contact::PhoneNumber* nb, contact->getPhoneNumbers()) {
272
foreach (PhoneNumber* nb, contact->phoneNumbers()) {
274
273
KABC::PhoneNumber pn;
275
pn.setType(nameToType(nb->getType()));
274
pn.setType(nameToType(nb->category()->name()));
277
pn.setNumber(nb->getNumber());
276
pn.setNumber(nb->uri());
278
277
newContact.insertPhoneNumber(pn);
298
298
///Implement virtual pure method
299
299
void AkonadiBackend::editContact(Contact* contact)
301
editContact(contact,0);
301
editContact(contact,nullptr);
304
304
///Implement virtual pure method
305
305
void AkonadiBackend::addNewContact(Contact* contact)
307
addNewContact(contact,0);
307
addNewContact(contact,nullptr);
310
310
///Add a new phone number to an existing contact
311
311
void AkonadiBackend::addPhoneNumber(Contact* contact, QString number, QString type)
313
Akonadi::Item item = m_ItemHash[contact->getUid()];
314
if (!(item.hasPayload<KABC::Addressee>() && item.payload<KABC::Addressee>().uid() == contact->getUid())) {
313
Akonadi::Item item = m_ItemHash[contact->uid()];
314
if (!(item.hasPayload<KABC::Addressee>() && item.payload<KABC::Addressee>().uid() == contact->uid())) {
315
315
kDebug() << "Contact not found";
322
322
QPointer<Akonadi::ContactEditor> editor = new Akonadi::ContactEditor( Akonadi::ContactEditor::EditMode, (QWidget*)nullptr );
323
323
editor->loadContact(item);
325
QPointer<KDialog> dlg = new KDialog(0);
325
QPointer<KDialog> dlg = new KDialog(nullptr);
326
326
dlg->setMainWidget(editor);
328
if ( !editor->saveContact() ) {
329
kDebug() << "Unable to save new contact to storage";
327
if ( dlg->exec() == QDialog::Accepted ) {
328
if ( !editor->saveContact() ) {
330
kDebug() << "Unable to save new contact to storage";
350
352
foreach (const Akonadi::Collection& coll, list) {
354
m_pMonitor->setCollectionMonitored(coll,true);
352
355
emit collectionChanged();
359
///Callback when a new item is added
360
void AkonadiBackend::slotItemAdded(Akonadi::Item item,Akonadi::Collection coll)
363
Contact* c = addItem(item,ConfigurationSkeleton::hideContactWithoutPhone());
364
if (c) { //Not all items will have an addressee payload
365
beginInsertRows(QModelIndex(),m_pContacts.size()-1,m_pContacts.size());
368
emit newContactAdded(c);
369
emit layoutChanged();
373
///Callback when an item change
374
void AkonadiBackend::slotItemChanged(const Akonadi::Item &item, const QSet< QByteArray > &part)
377
if (item.hasPayload<KABC::Addressee>()) {
378
KABC::Addressee tmp = item.payload<KABC::Addressee>();
379
Contact* c = getContactByUid(tmp.uid());
385
///Callback when a contact is removed
386
void AkonadiBackend::slotItemRemoved(const Akonadi::Item &item)
388
Contact* c = getContactByUid(item.remoteId());
356
393
///Update the contact list even without a new collection
357
394
ContactList AkonadiBackend::update_slot()