make webinterface translatable. there are around 20 short strings, all with context...
[kdenetwork.git] / kopete / libkopete / kabcpersistence.cpp
blob05db29567047f586c33c6ccd5aedad1309a01202
1 /*
2 addressbooklink.cpp - Manages operations involving the KDE Address Book
4 Copyright (c) 2005 Will Stephenson <wstephenson@kde.org>
6 Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
8 *************************************************************************
9 * *
10 * This library is free software; you can redistribute it and/or *
11 * modify it under the terms of the GNU Lesser General Public *
12 * License as published by the Free Software Foundation; either *
13 * version 2 of the License, or (at your option) any later version. *
14 * *
15 *************************************************************************
18 #include <qstring.h>
19 #include <qtimer.h>
21 #include <kabc/addressbook.h>
22 #include <kabc/addressee.h>
23 #include <kabc/resource.h>
24 #include <kabc/stdaddressbook.h>
26 // UI related includes used for importing from KABC
27 #include <kdialog.h>
28 #include <klocale.h>
29 #include <kmessagebox.h>
30 #include "accountselector.h"
31 #include "kopeteuiglobal.h"
33 #include "kopeteaccount.h"
34 #include "kopeteaccountmanager.h"
35 #include "kopetecontact.h"
36 #include "kopetemetacontact.h"
37 #include "kopetepluginmanager.h"
38 #include "kopeteprotocol.h"
40 #include "kabcpersistence.h"
42 namespace Kopete
45 /**
46 * utility function to merge two QStrings containing individual elements separated by 0xE000
48 static QString unionContents( QString arg1, QString arg2 )
50 QChar separator( 0xE000 );
51 QStringList outList = arg1.split( separator, QString::SkipEmptyParts );
52 QStringList arg2List = arg2.split( separator, QString::SkipEmptyParts );
53 for ( QStringList::iterator it = arg2List.begin(); it != arg2List.end(); ++it )
54 if ( !outList.contains( *it ) )
55 outList.append( *it );
56 QString out = outList.join( QString( separator ) );
57 return out;
60 class KABCPersistence::Private
62 public:
63 Private()
64 : addrBookWritePending(false)
66 QList<KABC::Resource *> pendingResources;
67 bool addrBookWritePending;
69 // FIXME: Try to remove that static variable !
70 static KABC::AddressBook* s_addressBook;
73 KABC::AddressBook* KABCPersistence::Private::s_addressBook = 0L;
75 KABCPersistence::KABCPersistence( QObject * parent, const char * name ) : QObject( parent)
77 setObjectName( name );
78 d = new Private;
81 KABCPersistence::~KABCPersistence()
83 delete d;
86 KABCPersistence *KABCPersistence::self()
88 static KABCPersistence s;
89 return &s;
92 KABC::AddressBook* KABCPersistence::addressBook()
94 if ( Private::s_addressBook == 0L )
96 Private::s_addressBook = KABC::StdAddressBook::self();
97 KABC::StdAddressBook::setAutomaticSave( false );
99 return Private::s_addressBook;
102 void KABCPersistence::write( MetaContact * mc )
104 // Save any changes in each contact's addressBookFields to KABC
105 KABC::AddressBook* ab = addressBook();
107 kDebug( 14010 ) << "looking up Addressee for " << mc->displayName() << "...";
108 // Look up the address book entry
109 KABC::Addressee theAddressee = ab->findByUid( mc->metaContactId() );
110 // Check that if addressee is not deleted or if the link is spurious
111 // (inherited from Kopete < 0.8, where all metacontacts had random ids)
112 if ( theAddressee.isEmpty() )
114 // not found in currently enabled addressbooks - may be in a disabled resource...
115 return;
117 else
119 // collate the instant messaging data to be inserted into the address book
120 QMap<QString, QStringList> addressMap;
121 QList<Contact *> contacts = mc->contacts();
122 QListIterator<Contact *> cIt( contacts );
123 while ( cIt.hasNext() )
125 Contact * c = cIt.next();
126 QStringList addresses = addressMap[ c->protocol()->addressBookIndexField() ];
127 addresses.append( c->contactId() );
128 addressMap.insert( c->protocol()->addressBookIndexField(), addresses );
131 // insert a custom field for each protocol
132 QMap<QString, QStringList>::ConstIterator it = addressMap.begin();
133 for ( ; it != addressMap.end(); ++it )
135 // read existing data for this key
136 QString currentCustomForProtocol = theAddressee.custom( it.key(), QLatin1String( "All" ) );
137 // merge without duplicating
138 QString toWrite = unionContents( currentCustomForProtocol, it.value().join( QString( QChar( 0xE000 ) ) ) );
139 // Note if nothing ends up in the KABC data, this is because insertCustom does nothing if any param is empty.
140 kDebug( 14010 ) << "Writing: " << it.key() << ", " << "All" << ", " << toWrite;
141 theAddressee.insertCustom( it.key(), QLatin1String( "All" ), toWrite );
142 QString check = theAddressee.custom( it.key(), QLatin1String( "All" ) );
144 ab->insertAddressee( theAddressee );
145 writeAddressBook( theAddressee.resource() );
146 //theAddressee.dump();
149 /* // Wipe out the existing addressBook entries
150 d->addressBook.clear();
151 // This causes each Kopete::Protocol subclass to serialise its contacts' data into the metacontact's plugin data and address book data
152 emit aboutToSave(this);
154 kDebug( 14010 ) << "...FOUND ONE!";
155 // Store address book fields
156 QMap<QString, QMap<QString, QString> >::ConstIterator appIt = d->addressBook.begin();
157 for( ; appIt != d->addressBook.end(); ++appIt )
159 QMap<QString, QString>::ConstIterator addrIt = appIt.data().begin();
160 for( ; addrIt != appIt.data().end(); ++addrIt )
162 // read existing data for this key
163 QString currentCustom = theAddressee.custom( appIt.key(), addrIt.key() );
164 // merge without duplicating
165 QString toWrite = unionContents( currentCustom, addrIt.data() );
166 // write the result
167 // Note if nothing ends up in the KABC data, this is because insertCustom does nothing if any param is empty.
168 kDebug( 14010 ) << "Writing: " << appIt.key() << ", " << addrIt.key() << ", " << toWrite;
169 theAddressee.insertCustom( appIt.key(), addrIt.key(), toWrite );
172 ab->insertAddressee( theAddressee );
173 writeAddressBook();
177 void KABCPersistence::writeAddressBook( KABC::Resource * res)
179 if ( !d->pendingResources.count( res ) )
180 d->pendingResources.append( res );
181 if ( !d->addrBookWritePending )
183 d->addrBookWritePending = true;
184 QTimer::singleShot( 2000, this, SLOT( slotWriteAddressBook() ) );
188 void KABCPersistence::slotWriteAddressBook()
190 //kDebug( 14010 ) ;
191 KABC::AddressBook* ab = addressBook();
192 QListIterator<KABC::Resource *> it( d->pendingResources );
193 while ( it.hasNext() )
195 //kDebug( 14010 ) << "Writing resource " << it.current()->resourceName();
196 KABC::Ticket *ticket = ab->requestSaveTicket( it.next() );
197 if ( !ticket )
198 kWarning( 14010 ) << "WARNING: Resource is locked by other application!";
199 else
201 if ( !ab->save( ticket ) )
203 kWarning( 14010 ) << "ERROR: Saving failed!";
204 ab->releaseSaveTicket( ticket );
207 //kDebug( 14010 ) << "Finished writing KABC";
209 d->pendingResources.clear();
210 d->addrBookWritePending = false;
213 void KABCPersistence::removeKABC( MetaContact *)
215 /* // remove any data this KMC has written to the KDE address book
216 // Save any changes in each contact's addressBookFields to KABC
217 KABC::AddressBook* ab = addressBook();
219 // Wipe out the existing addressBook entries
220 d->addressBook.clear();
221 // This causes each Kopete::Protocol subclass to serialise its contacts' data into the metacontact's plugin data and address book data
222 emit aboutToSave(this);
224 // If the metacontact is linked to a kabc entry
225 if ( !d->metaContactId.isEmpty() )
227 //kDebug( 14010 ) << "looking up Addressee for " << displayName() << "...";
228 // Look up the address book entry
229 KABC::Addressee theAddressee = ab->findByUid( metaContactId() );
231 if ( theAddressee.isEmpty() )
233 // remove the link
234 //kDebug( 14010 ) << "...not found.";
235 d->metaContactId.clear();
237 else
239 //kDebug( 14010 ) << "...FOUND ONE!";
240 // Remove address book fields
241 QMap<QString, QMap<QString, QString> >::ConstIterator appIt = d->addressBook.begin();
242 for( ; appIt != d->addressBook.end(); ++appIt )
244 QMap<QString, QString>::ConstIterator addrIt = appIt.data().begin();
245 for( ; addrIt != appIt.data().end(); ++addrIt )
247 // FIXME: This assumes Kopete is the only app writing these fields
248 kDebug( 14010 ) << "Removing: " << appIt.key() << ", " << addrIt.key();
249 theAddressee.removeCustom( appIt.key(), addrIt.key() );
252 ab->insertAddressee( theAddressee );
254 writeAddressBook();
257 // kDebug(14010) << kBacktrace();*/
260 bool KABCPersistence::syncWithKABC( MetaContact * mc )
262 kDebug(14010) ;
263 bool contactAdded = false;
264 // check whether the dontShowAgain was checked
265 KABC::AddressBook* ab = addressBook();
266 KABC::Addressee addr = ab->findByUid( mc->metaContactId() );
268 if ( !addr.isEmpty() ) // if we are associated with KABC
270 // load the set of addresses from KABC
271 QStringList customs = addr.customs();
273 QStringList::ConstIterator it;
274 for ( it = customs.begin(); it != customs.end(); ++it )
276 QString app, name, value;
277 splitField( *it, app, name, value );
278 kDebug( 14010 ) << "app=" << app << " name=" << name << " value=" << value;
280 if ( app.startsWith( QLatin1String( "messaging/" ) ) )
282 if ( name == QLatin1String( "All" ) )
284 kDebug( 14010 ) << " syncing \"" << app << ":" << name << " with contact list ";
285 // Get the protocol name from the custom field
286 // by chopping the 'messaging/' prefix from the custom field app name
287 QString protocolName = app.right( app.length() - 10 );
288 // munge Jabber hack
289 if ( protocolName == QLatin1String( "xmpp" ) )
290 protocolName = QLatin1String( "jabber" );
292 // Check Kopete supports it
293 Protocol * proto = dynamic_cast<Protocol*>( PluginManager::self()->loadPlugin( QLatin1String( "kopete_" ) + protocolName ) );
294 if ( !proto )
296 KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Sorry,
297 i18n( "<qt>\"%1\" is not supported by Kopete.</qt>", protocolName ),
298 i18n( "Could Not Sync with KDE Address Book" ) );
299 continue;
302 // See if we need to add each contact in this protocol
303 QStringList addresses = value.split( QChar( 0xE000 ), QString::SkipEmptyParts );
304 QStringList::iterator end = addresses.end();
305 for ( QStringList::iterator it = addresses.begin(); it != end; ++it )
307 // check whether each one is present in Kopete
308 // Is it in the contact list?
309 // First discard anything after an 0xE120, this is used by IRC to separate nick and server group name, but
310 // IRC doesn't support this properly yet, so the user will have to select an appropriate account manually
311 int separatorPos = (*it).indexOf( QChar( 0xE120 ) );
312 if ( separatorPos != -1 )
313 *it = (*it).left( separatorPos );
315 Kopete::MetaContact *otherMc = 0;
316 foreach( Kopete::Account *act, Kopete::AccountManager::self()->accounts() )
318 if( act->protocol() != proto )
319 continue;
320 Kopete::Contact *c= act->contacts()[*it];
321 if(c)
323 otherMc=c->metaContact();
324 break;
328 if ( otherMc ) // Is it in another metacontact?
330 // Is it already in this metacontact? If so, we needn't do anything
331 if ( otherMc == mc )
333 kDebug( 14010 ) << *it << " already a child of this metacontact.";
334 continue;
336 kDebug( 14010 ) << *it << " already exists in OTHER metacontact, move here?";
337 // find the Kopete::Contact and attempt to move it to this metacontact.
338 otherMc->findContact( proto->pluginId(), QString(), *it )->setMetaContact( mc );
340 else
342 // if not, prompt to add it
343 kDebug( 14010 ) << proto->pluginId() << "://" << *it << " was not found in the contact list. Prompting to add...";
344 if ( KMessageBox::Yes == KMessageBox::questionYesNo( Kopete::UI::Global::mainWidget(),
345 i18n( "<qt>An address was added to this contact by another application.<br />Would you like to use it in Kopete?<br /><b>Protocol:</b> %1<br /><b>Address:</b> %2</qt>", proto->displayName(), *it ), i18n( "Import Address From Address Book" ), KGuiItem( i18n("Use") ), KGuiItem( i18n("Do Not Use") ), QLatin1String( "ImportFromKABC" ) ) )
347 // Check the accounts for this protocol are all connected
348 // Most protocols do not allow you to add contacts while offline
349 // Would be better to have a virtual bool Kopete::Account::readyToAddContact()
350 int accountcount=0;
351 bool allAccountsConnected = true;
352 Kopete::Account *chosen = 0;
353 foreach( Kopete::Account *act, Kopete::AccountManager::self()->accounts() )
355 if( act->protocol() == proto)
357 accountcount++;
358 if(!act->isConnected())
360 allAccountsConnected=false;
361 break;
363 chosen=act;
367 if ( !allAccountsConnected )
369 KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Sorry,
370 i18n( "<qt>One or more of your accounts using %1 are offline. Most systems have to be connected to add contacts. Please connect these accounts and try again.</qt>", protocolName ),
371 i18n( "Not Connected" ) );
372 continue;
375 // we have got a contact to add, our accounts are connected, so add it.
376 // Do we need to choose an account
377 if ( accountcount > 1 )
378 { // if we have >1 account in this protocol, prompt for the protocol.
379 KDialog *chooser = new KDialog(0);
380 chooser->setCaption( i18n("Choose Account") );
381 chooser->setButtons( KDialog::Ok | KDialog::Cancel );
383 AccountSelector *accSelector = new AccountSelector(proto, chooser);
384 accSelector->setObjectName( QLatin1String("accSelector") );
385 chooser->setMainWidget(accSelector);
386 if ( chooser->exec() == QDialog::Rejected )
387 continue;
388 chosen = accSelector->selectedItem();
390 delete chooser;
392 else if ( accountcount == 0 )
394 KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Sorry,
395 i18n( "<qt>You do not have an account configured for <b>%1</b> yet. Please create an account, connect it, and try again.</qt>", protocolName ),
396 i18n( "No Account Found" ) );
397 continue;
400 // add the contact to the chosen account
401 if ( chosen )
403 kDebug( 14010 ) << "Adding " << *it << " to " << chosen->accountId();
404 if ( chosen->addContact( *it, mc ) )
405 contactAdded = true;
406 else
407 KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Sorry,
408 i18n( "<qt>It was not possible to add the contact.</qt>" ),
409 i18n( "Could Not Add Contact") ) ;
412 else
413 kDebug( 14010 ) << " user declined to add " << *it << " to contact list ";
416 kDebug( 14010 ) << " all " << addresses.count() << " contacts in " << proto->pluginId() << " checked ";
418 else
419 kDebug( 14010 ) << "not interested in name=" << name;
422 else
423 kDebug( 14010 ) << "not interested in app=" << app;
426 return contactAdded;
427 return false;
430 // FIXME: Remove when IM address API is in KABC (KDE 4)
431 void KABCPersistence::splitField( const QString &str, QString &app, QString &name, QString &value )
433 int colon = str.indexOf( ':' );
434 if ( colon != -1 ) {
435 QString tmp = str.left( colon );
436 value = str.mid( colon + 1 );
438 int dash = tmp.indexOf( '-' );
439 if ( dash != -1 ) {
440 app = tmp.left( dash );
441 name = tmp.mid( dash + 1 );
446 } // end namespace Kopete
448 // dump addressbook contents
450 #include "kabcpersistence.moc"