4
Kopete Web Presence plugin
6
Copyright 2005-2006 by Tommi Rantala <tommi.rantala@cs.helsinki.fi>
7
Copyright (c) 2002,2003 by Will Stephenson <will@stevello.free-online.co.uk>
9
Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
11
*************************************************************************
13
* This program is free software; you can redistribute it and/or modify *
14
* it under the terms of the GNU General Public License as published by *
15
* the Free Software Foundation; either version 2 of the License, or *
16
* (at your option) any later version. *
18
*************************************************************************
21
#include <config-kopete.h> // for HAVE_XSLT
23
#include "webpresenceplugin.h"
25
#include <QtCore/QTimer>
26
#include <QtCore/QFile>
27
#include <QtCore/QTextCodec>
28
#include <QtCore/QTextStream>
29
#include <QtCore/QList>
30
#include <QtCore/QDateTime>
31
#include <QtXml/QDomDocument>
34
#include <kgenericfactory.h>
36
#include <kmessagebox.h>
37
#include <ktemporaryfile.h>
38
#include <kstandarddirs.h>
41
#include <libxml/parser.h>
42
#include <libxml/tree.h>
44
#include <libxslt/xsltconfig.h>
45
#include <libxslt/xsltInternals.h>
46
#include <libxslt/transform.h>
47
#include <libxslt/xsltutils.h>
50
#include "kopetepluginmanager.h"
51
#include "kopeteprotocol.h"
52
#include "kopeteaccountmanager.h"
53
#include "kopeteaccount.h"
54
#include "kopetecontact.h"
56
#include "webpresenceconfig.h"
58
K_PLUGIN_FACTORY(WebPresencePluginFactory, registerPlugin<WebPresencePlugin>();)
59
K_EXPORT_PLUGIN(WebPresencePluginFactory( "kopete_webpresence" ))
62
WebPresencePlugin::WebPresencePlugin( QObject *parent, const QVariantList& /*args*/ )
63
: Kopete::Plugin( WebPresencePluginFactory::componentData(), parent ),
64
shuttingDown( false ), resultFormatting( WEB_HTML ), m_output(0)
66
m_writeScheduler = new QTimer( this );
67
connect ( m_writeScheduler, SIGNAL(timeout()), this, SLOT(slotWriteFile()) );
68
connect( Kopete::AccountManager::self(), SIGNAL(accountRegistered(Kopete::Account*)),
69
this, SLOT(listenToAllAccounts()) );
70
connect( Kopete::AccountManager::self(), SIGNAL(accountUnregistered(const Kopete::Account*)),
71
this, SLOT(listenToAllAccounts()) );
73
connect(this, SIGNAL(settingsChanged()), this, SLOT(slotSettingsChanged()) );
74
slotSettingsChanged();
75
listenToAllAccounts();
78
WebPresencePlugin::~WebPresencePlugin()
83
void WebPresencePlugin::slotSettingsChanged()
85
// Force reading config
86
WebPresenceConfig::self()->readConfig();
88
resultFormatting = WEB_UNDEFINED;
90
if ( WebPresenceConfig::self()->formatHTML() ) {
91
resultFormatting = WEB_HTML;
92
} else if ( WebPresenceConfig::self()->formatXHTML() ) {
93
resultFormatting = WEB_XHTML;
94
} else if ( WebPresenceConfig::self()->formatXML() ) {
95
resultFormatting = WEB_XML;
96
} else if ( WebPresenceConfig::self()->formatStylesheet() ) {
97
resultFormatting = WEB_CUSTOM;
98
userStyleSheet = WebPresenceConfig::self()->formatStylesheetURL();
101
// Default to HTML, if we don't get anything useful from config file.
102
if ( resultFormatting == WEB_UNDEFINED )
103
resultFormatting = WEB_HTML;
109
void WebPresencePlugin::listenToAllAccounts()
111
// Connect to signals notifying of all accounts' status changes.
112
ProtocolList protocols = allProtocols();
114
for ( ProtocolList::Iterator it = protocols.begin();
115
it != protocols.end(); ++it )
117
QList<Kopete::Account*> accounts = Kopete::AccountManager::self()->accounts( *it );
118
foreach(Kopete::Account *account, accounts)
120
listenToAccount( account );
123
slotWaitMoreStatusChanges();
126
void WebPresencePlugin::listenToAccount( Kopete::Account* account )
128
if(account && account->myself())
130
// Connect to the account's status changed signal
131
// because we can't know if the account has already connected
132
QObject::disconnect( account->myself(),
133
SIGNAL(onlineStatusChanged( Kopete::Contact *,
134
const Kopete::OnlineStatus &,
135
const Kopete::OnlineStatus & ) ),
137
SLOT(slotWaitMoreStatusChanges()) ) ;
138
QObject::connect( account->myself(),
139
SIGNAL(onlineStatusChanged( Kopete::Contact *,
140
const Kopete::OnlineStatus &,
141
const Kopete::OnlineStatus & ) ),
143
SLOT(slotWaitMoreStatusChanges()) );
147
void WebPresencePlugin::slotWaitMoreStatusChanges()
149
if ( !m_writeScheduler->isActive() )
150
m_writeScheduler->start( WebPresenceConfig::self()->uploadFrequency() * 1000 );
153
void WebPresencePlugin::slotWriteFile()
155
m_writeScheduler->stop();
157
// generate the (temporary) XML file representing the current contact list
158
const KUrl dest = WebPresenceConfig::self()->uploadURL();
159
if ( dest.isEmpty() || !dest.isValid() )
161
kDebug(14309) << "url is empty or not valid. NOT UPDATING!";
165
KTemporaryFile* xml = generateFile();
166
xml->setAutoRemove( true );
168
switch( resultFormatting ) {
176
m_output = new KTemporaryFile();
179
if ( !transform( xml, m_output ) )
181
//TODO: give some error to user, even better if shown only once
189
delete xml; // might make debugging harder!
195
// upload it to the specified URL
196
KUrl src( m_output->fileName() );
197
KIO::FileCopyJob *job = KIO::file_move( src, dest, -1, KIO::Overwrite | KIO::HideProgressInfo );
198
connect( job, SIGNAL(result(KJob*)),
199
SLOT(slotUploadJobResult(KJob*)) );
202
void WebPresencePlugin::slotUploadJobResult( KJob *job )
204
if ( job->error() ) {
205
kDebug(14309) << "Error uploading presence info.";
206
KMessageBox::queuedDetailedError( 0, i18n("An error occurred when uploading your presence page.\nCheck the path and write permissions of the destination."), 0, displayName() );
212
KTemporaryFile* WebPresencePlugin::generateFile()
214
// generate the (temporary) XML file representing the current contact list
216
QString notKnown = i18n( "Not yet known" );
220
doc.appendChild( doc.createProcessingInstruction( "xml",
221
"version=\"1.0\" encoding=\"UTF-8\"" ) );
223
QDomElement root = doc.createElement( "webpresence" );
224
doc.appendChild( root );
226
// insert the current date/time
227
QDomElement date = doc.createElement( "listdate" );
228
QDomText t = doc.createTextNode(
229
KGlobal::locale()->formatDateTime( QDateTime::currentDateTime() ) );
230
date.appendChild( t );
231
root.appendChild( date );
233
// insert the user's name
234
QDomElement name = doc.createElement( "name" );
236
const QString userName = WebPresenceConfig::self()->showThisName();
237
if ( !WebPresenceConfig::self()->showName() && !userName.isEmpty() )
238
nameText = doc.createTextNode( userName );
240
nameText = doc.createTextNode( notKnown );
241
name.appendChild( nameText );
242
root.appendChild( name );
244
// insert the list of the user's accounts
245
QDomElement accounts = doc.createElement( "accounts" );
246
root.appendChild( accounts );
248
QList<Kopete::Account*> list = Kopete::AccountManager::self()->accounts();
249
// If no accounts, stop here
250
if ( !list.isEmpty() )
252
foreach(Kopete::Account *account, list)
254
QDomElement acc = doc.createElement( "account" );
255
//output += h.openTag( "account" );
257
QDomElement protoName = doc.createElement( "protocol" );
258
QDomText protoNameText = doc.createTextNode(
259
account->protocol()->pluginId() );
260
protoName.appendChild( protoNameText );
261
acc.appendChild( protoName );
263
Kopete::Contact* me = account->myself();
264
QString displayName = me->property( Kopete::Global::Properties::self()->nickName() ).value().toString();
265
QDomElement accName = doc.createElement( "accountname" );
266
QDomText accNameText = doc.createTextNode( ( me )
269
accName.appendChild( accNameText );
270
acc.appendChild( accName );
272
QDomElement accStatus = doc.createElement( "accountstatus" );
273
QDomText statusText = doc.createTextNode( ( me )
274
? statusAsString( me->onlineStatus() )
276
accStatus.appendChild( statusText );
278
// Do not add these if we're shutting down, because the result
279
// would be quite weird.
280
if ( !shuttingDown ) {
282
// Add away message as an attribute, if one exists.
283
if ( (me->onlineStatus().status() == Kopete::OnlineStatus::Away ||
284
me->onlineStatus().status() == Kopete::OnlineStatus::Busy) &&
285
!me->property("awayMessage").value().toString().isEmpty() ) {
286
accStatus.setAttribute( "awayreason",
287
me->property("awayMessage").value().toString() );
290
// Add the online status description as an attribute, if one exits.
291
if ( !me->onlineStatus().description().isEmpty() ) {
292
accStatus.setAttribute( "statusdescription",
293
me->onlineStatus().description() );
296
acc.appendChild( accStatus );
298
if ( WebPresenceConfig::self()->includeIMAddress() )
300
QDomElement accAddress = doc.createElement( "accountaddress" );
301
QDomText addressText = doc.createTextNode( ( me )
304
accAddress.appendChild( addressText );
305
acc.appendChild( accAddress );
308
accounts.appendChild( acc );
312
// write the XML to a temporary file
313
KTemporaryFile* file = new KTemporaryFile();
314
file->setAutoRemove(false);
316
QTextStream stream( file );
317
stream.setCodec(QTextCodec::codecForName("UTF-8"));
318
doc.save( stream, 4 );
323
bool WebPresencePlugin::transform( KTemporaryFile * src, KTemporaryFile * dest )
327
xmlSubstituteEntitiesDefault( 1 );
328
xmlLoadExtDtdDefaultValue = 1;
332
switch ( resultFormatting ) {
334
// Oops! We tried to call transform() but XML was requested.
337
if ( WebPresenceConfig::self()->useImagesHTML() ) {
338
sheet.setFileName( KStandardDirs::locate( "appdata", "webpresence/webpresence_html_images.xsl" ) );
340
sheet.setFileName( KStandardDirs::locate( "appdata", "webpresence/webpresence_html.xsl" ) );
344
if ( WebPresenceConfig::self()->useImagesHTML() ) {
345
sheet.setFileName( KStandardDirs::locate( "appdata", "webpresence/webpresence_xhtml_images.xsl" ) );
347
sheet.setFileName( KStandardDirs::locate( "appdata", "webpresence/webpresence_xhtml.xsl" ) );
351
sheet.setFileName( userStyleSheet.path() );
354
// Shouldn't ever reach here.
358
// TODO: auto / smart pointers would be useful here
359
xsltStylesheetPtr cur = 0;
363
if ( !sheet.exists() ) {
364
kDebug(14309) << "ERROR: Style sheet not found";
370
cur = xsltParseStylesheetFile( (const xmlChar *) sheet.fileName().toLatin1().data() );
372
kDebug(14309) << "ERROR: Style sheet parsing failed";
377
doc = xmlParseFile( QFile::encodeName( src->fileName() ) );
379
kDebug(14309) << "ERROR: XML parsing failed";
384
res = xsltApplyStylesheet( cur, doc, 0 );
386
kDebug(14309) << "ERROR: Style sheet apply failed";
392
if ( xsltSaveResultToFd(dest->handle(), res, cur) == -1 ) {
393
kDebug(14309) << "ERROR: Style sheet apply failed";
398
// then it all worked!
401
xsltCleanupGlobals();
403
if (doc) xmlFreeDoc(doc);
404
if (res) xmlFreeDoc(res);
405
if (cur) xsltFreeStylesheet(cur);
417
ProtocolList WebPresencePlugin::allProtocols()
421
Kopete::PluginList plugins = Kopete::PluginManager::self()->loadedPlugins( "Protocols" );
422
Kopete::PluginList::ConstIterator it;
426
for ( it = plugins.constBegin(); it != plugins.constEnd(); ++it ) {
427
result.append( static_cast<Kopete::Protocol *>( *it ) );
433
QString WebPresencePlugin::statusAsString( const Kopete::OnlineStatus &newStatus )
439
switch ( newStatus.status() )
441
case Kopete::OnlineStatus::Online:
444
case Kopete::OnlineStatus::Away:
447
case Kopete::OnlineStatus::Busy:
450
case Kopete::OnlineStatus::Offline:
451
case Kopete::OnlineStatus::Invisible:
461
void WebPresencePlugin::aboutToUnload()
463
// Stop timer. Do not need it anymore.
464
m_writeScheduler->stop();
466
// Force statusAsString() report all accounts as OFFLINE.
469
// Do final update of webpresence file.
472
emit readyForUnload();
475
// vim: set noet ts=4 sts=4 sw=4:
476
#include "webpresenceplugin.moc"