diff --git a/plugins/webpresence/webpresenceplugin.cpp b/plugins/webpresence/webpresenceplugin.cpp index ca4c2e7ac..5d5a5e3c3 100644 --- a/plugins/webpresence/webpresenceplugin.cpp +++ b/plugins/webpresence/webpresenceplugin.cpp @@ -1,455 +1,460 @@ /* webpresenceplugin.cpp Kopete Web Presence plugin Copyright 2005-2006 by Tommi Rantala Copyright (c) 2002,2003 by Will Stephenson Kopete (c) 2002-2006 by the Kopete developers ************************************************************************* * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ************************************************************************* */ #include "webpresenceplugin.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kopetepluginmanager.h" #include "kopeteprotocol.h" #include "kopeteaccountmanager.h" #include "kopeteaccount.h" #include "kopetecontact.h" #include "webpresenceconfig.h" K_PLUGIN_FACTORY(WebPresencePluginFactory, registerPlugin(); ) K_EXPORT_PLUGIN(WebPresencePluginFactory("kopete_webpresence")) WebPresencePlugin::WebPresencePlugin(QObject *parent, const QVariantList & /*args*/) : Kopete::Plugin(parent) , shuttingDown(false) , resultFormatting(WEB_HTML) , m_output(0) { m_writeScheduler = new QTimer(this); connect(m_writeScheduler, &QTimer::timeout, this, &WebPresencePlugin::slotWriteFile); connect(Kopete::AccountManager::self(), &Kopete::AccountManager::accountRegistered, this, &WebPresencePlugin::listenToAllAccounts); connect(Kopete::AccountManager::self(), &Kopete::AccountManager::accountUnregistered, this, &WebPresencePlugin::listenToAllAccounts); connect(this, &WebPresencePlugin::settingsChanged, this, &WebPresencePlugin::slotSettingsChanged); slotSettingsChanged(); listenToAllAccounts(); } WebPresencePlugin::~WebPresencePlugin() { delete m_output; } void WebPresencePlugin::slotSettingsChanged() { // Force reading config WebPresenceConfig::self()->load(); resultFormatting = WEB_UNDEFINED; if (WebPresenceConfig::self()->formatHTML()) { resultFormatting = WEB_HTML; } else if (WebPresenceConfig::self()->formatXHTML()) { resultFormatting = WEB_XHTML; } else if (WebPresenceConfig::self()->formatXML()) { resultFormatting = WEB_XML; } else if (WebPresenceConfig::self()->formatStylesheet()) { resultFormatting = WEB_CUSTOM; userStyleSheet = WebPresenceConfig::self()->formatStylesheetURL(); } // Default to HTML, if we don't get anything useful from config file. if (resultFormatting == WEB_UNDEFINED) { resultFormatting = WEB_HTML; } // Update file slotWriteFile(); } void WebPresencePlugin::listenToAllAccounts() { // Connect to signals notifying of all accounts' status changes. ProtocolList protocols = allProtocols(); for (ProtocolList::Iterator it = protocols.begin(); it != protocols.end(); ++it) { QList accounts = Kopete::AccountManager::self()->accounts(*it); foreach (Kopete::Account *account, accounts) { listenToAccount(account); } } slotWaitMoreStatusChanges(); } void WebPresencePlugin::listenToAccount(Kopete::Account *account) { if (account && account->myself()) { // Connect to the account's status changed signal // because we can't know if the account has already connected - QObject::disconnect(account->myself(), SIGNAL(onlineStatusChanged(Kopete::Contact *, const Kopete::OnlineStatus&, const Kopete::OnlineStatus&)), this, SLOT(slotWaitMoreStatusChanges())); - QObject::connect(account->myself(), SIGNAL(onlineStatusChanged(Kopete::Contact *, const Kopete::OnlineStatus&, const Kopete::OnlineStatus&)), this, SLOT(slotWaitMoreStatusChanges())); + QObject::disconnect(account->myself(), &Kopete::Contact::onlineStatusChanged, this, &WebPresencePlugin::slotRecieveStatusUpdate); + QObject::connect(account->myself(), &Kopete::Contact::onlineStatusChanged, this, &WebPresencePlugin::slotRecieveStatusUpdate); } } +void WebPresencePlugin::slotRecieveStatusUpdate(Kopete::Contact * /* args */, const Kopete::OnlineStatus& /* args */, const Kopete::OnlineStatus& /* args */) +{ + slotWaitMoreStatusChanges(); +} + void WebPresencePlugin::slotWaitMoreStatusChanges() { if (!m_writeScheduler->isActive()) { m_writeScheduler->start(WebPresenceConfig::self()->uploadFrequency() * 1000); } } void WebPresencePlugin::slotWriteFile() { m_writeScheduler->stop(); // generate the (temporary) XML file representing the current contact list - const QUrl dest = WebPresenceConfig::self()->uploadURL(); + const QUrl dest = QUrl::fromLocalFile(WebPresenceConfig::self()->uploadURL().path()); if (dest.isEmpty() || !dest.isValid()) { kDebug(14309) << "url is empty or not valid. NOT UPDATING!"; return; } QTemporaryFile *xml = generateFile(); xml->setAutoRemove(true); switch (resultFormatting) { case WEB_XML: m_output = xml; xml = nullptr; break; case WEB_HTML: case WEB_XHTML: case WEB_CUSTOM: m_output = new QTemporaryFile(); m_output->open(); if (!transform(xml, m_output)) { //TODO: give some error to user, even better if shown only once delete m_output; m_output = nullptr; delete xml; return; } delete xml; // might make debugging harder! break; default: return; } // upload it to the specified URL - QUrl src(m_output->fileName()); + QUrl src = QUrl::fromLocalFile(m_output->fileName()); KIO::FileCopyJob *job = KIO::file_move(src, dest, -1, KIO::Overwrite | KIO::HideProgressInfo); connect(job, &KIO::FileCopyJob::result, this, &WebPresencePlugin::slotUploadJobResult); } void WebPresencePlugin::slotUploadJobResult(KJob *job) { if (job->error()) { kDebug(14309) << "Error uploading presence info."; KMessageBox::detailedError(0, i18n("An error occurred when uploading your presence page.\nCheck the path and write permissions of the destination."), 0, displayName()); delete m_output; m_output = nullptr; } } QTemporaryFile *WebPresencePlugin::generateFile() { // generate the (temporary) XML file representing the current contact list kDebug(14309); QString notKnown = i18n("Not yet known"); QDomDocument doc; doc.appendChild(doc.createProcessingInstruction(QStringLiteral("xml"), QStringLiteral("version=\"1.0\" encoding=\"UTF-8\""))); QDomElement root = doc.createElement(QStringLiteral("webpresence")); doc.appendChild(root); // insert the current date/time QDomElement date = doc.createElement(QStringLiteral("listdate")); QDomText t = doc.createTextNode(QLocale().toString(QDateTime::currentDateTime())); date.appendChild(t); root.appendChild(date); // insert the user's name QDomElement name = doc.createElement(QStringLiteral("name")); QDomText nameText; const QString userName = WebPresenceConfig::self()->showThisName(); if (!WebPresenceConfig::self()->showName() && !userName.isEmpty()) { nameText = doc.createTextNode(userName); } else { nameText = doc.createTextNode(notKnown); } name.appendChild(nameText); root.appendChild(name); // insert the list of the user's accounts QDomElement accounts = doc.createElement(QStringLiteral("accounts")); root.appendChild(accounts); QList list = Kopete::AccountManager::self()->accounts(); // If no accounts, stop here if (!list.isEmpty()) { foreach (Kopete::Account *account, list) { QDomElement acc = doc.createElement(QStringLiteral("account")); //output += h.openTag( "account" ); QDomElement protoName = doc.createElement(QStringLiteral("protocol")); QDomText protoNameText = doc.createTextNode( account->protocol()->pluginId()); protoName.appendChild(protoNameText); acc.appendChild(protoName); Kopete::Contact *me = account->myself(); QString displayName = me->displayName(); QDomElement accName = doc.createElement(QStringLiteral("accountname")); QDomText accNameText = doc.createTextNode((me) ? displayName : notKnown); accName.appendChild(accNameText); acc.appendChild(accName); QDomElement accStatus = doc.createElement(QStringLiteral("accountstatus")); QDomText statusText = doc.createTextNode((me) ? statusAsString(me->onlineStatus()) : notKnown); accStatus.appendChild(statusText); // Do not add these if we're shutting down, because the result // would be quite weird. if (!shuttingDown) { // Add away message as an attribute, if one exists. if ((me->onlineStatus().status() == Kopete::OnlineStatus::Away || me->onlineStatus().status() == Kopete::OnlineStatus::Busy) && !me->property(QStringLiteral("awayMessage")).value().toString().isEmpty()) { accStatus.setAttribute(QStringLiteral("awayreason"), me->property(QStringLiteral("awayMessage")).value().toString()); } // Add the online status description as an attribute, if one exits. if (!me->onlineStatus().description().isEmpty()) { accStatus.setAttribute(QStringLiteral("statusdescription"), me->onlineStatus().description()); } } acc.appendChild(accStatus); if (WebPresenceConfig::self()->includeIMAddress()) { QDomElement accAddress = doc.createElement(QStringLiteral("accountaddress")); QDomText addressText = doc.createTextNode((me) ? me->contactId() : notKnown); accAddress.appendChild(addressText); acc.appendChild(accAddress); } accounts.appendChild(acc); } } // write the XML to a temporary file QTemporaryFile *file = new QTemporaryFile(); file->setAutoRemove(false); file->open(); QString buf; QTextStream stream(&buf, QIODevice::WriteOnly); stream.setCodec("UTF-16"); // QtXML works only with UTF-16 doc.documentElement().save(stream, 4); // QDomDocument::save() override stream codec to UTF-8 file->write(buf.toUtf8()); file->flush(); return file; } bool WebPresencePlugin::transform(QTemporaryFile *src, QTemporaryFile *dest) { bool retval = true; xmlSubstituteEntitiesDefault(1); xmlLoadExtDtdDefaultValue = 1; QFile sheet; switch (resultFormatting) { case WEB_XML: // Oops! We tried to call transform() but XML was requested. return false; case WEB_HTML: if (WebPresenceConfig::self()->useImagesHTML()) { sheet.setFileName(QStandardPaths::locate(QStandardPaths::DataLocation, QStringLiteral("/webpresence/webpresence_html_images.xsl"))); } else { sheet.setFileName(QStandardPaths::locate(QStandardPaths::DataLocation, QStringLiteral("/webpresence/webpresence_html.xsl"))); } break; case WEB_XHTML: if (WebPresenceConfig::self()->useImagesHTML()) { sheet.setFileName(QStandardPaths::locate(QStandardPaths::DataLocation, QStringLiteral("/webpresence/webpresence_xhtml_images.xsl"))); } else { sheet.setFileName(QStandardPaths::locate(QStandardPaths::DataLocation, QStringLiteral("/webpresence/webpresence_xhtml.xsl"))); } break; case WEB_CUSTOM: sheet.setFileName(userStyleSheet.path()); break; default: // Shouldn't ever reach here. return false; } // TODO: auto / smart pointers would be useful here xsltStylesheetPtr cur = 0; xmlDocPtr doc = 0; xmlDocPtr res = 0; if (!sheet.exists()) { kDebug(14309) << "ERROR: Style sheet not found"; retval = false; goto end; } // is the cast safe? cur = xsltParseStylesheetFile((const xmlChar *)sheet.fileName().toLatin1().data()); if (!cur) { kDebug(14309) << "ERROR: Style sheet parsing failed"; retval = false; goto end; } doc = xmlParseFile(QFile::encodeName(src->fileName())); if (!doc) { kDebug(14309) << "ERROR: XML parsing failed"; retval = false; goto end; } res = xsltApplyStylesheet(cur, doc, 0); if (!res) { kDebug(14309) << "ERROR: Style sheet apply failed"; retval = false; goto end; } if (xsltSaveResultToFd(dest->handle(), res, cur) == -1) { kDebug(14309) << "ERROR: Style sheet apply failed"; retval = false; goto end; } // then it all worked! end: xsltCleanupGlobals(); xmlCleanupParser(); if (doc) { xmlFreeDoc(doc); } if (res) { xmlFreeDoc(res); } if (cur) { xsltFreeStylesheet(cur); } return retval; } ProtocolList WebPresencePlugin::allProtocols() { kDebug(14309); Kopete::PluginList plugins = Kopete::PluginManager::self()->loadedPlugins(QStringLiteral("Protocols")); Kopete::PluginList::ConstIterator it; ProtocolList result; for (it = plugins.constBegin(); it != plugins.constEnd(); ++it) { result.append(static_cast(*it)); } return result; } QString WebPresencePlugin::statusAsString(const Kopete::OnlineStatus &newStatus) { if (shuttingDown) { return QStringLiteral("OFFLINE"); } QString status; switch (newStatus.status()) { case Kopete::OnlineStatus::Online: status = QStringLiteral("ONLINE"); break; case Kopete::OnlineStatus::Away: status = QStringLiteral("AWAY"); break; case Kopete::OnlineStatus::Busy: status = QStringLiteral("BUSY"); break; case Kopete::OnlineStatus::Offline: case Kopete::OnlineStatus::Invisible: status = QStringLiteral("OFFLINE"); break; default: status = QStringLiteral("UNKNOWN"); } return status; } void WebPresencePlugin::aboutToUnload() { // Stop timer. Do not need it anymore. m_writeScheduler->stop(); // Force statusAsString() report all accounts as OFFLINE. shuttingDown = true; // Do final update of webpresence file. slotWriteFile(); emit readyForUnload(); } // vim: set noet ts=4 sts=4 sw=4: #include "webpresenceplugin.moc" diff --git a/plugins/webpresence/webpresenceplugin.h b/plugins/webpresence/webpresenceplugin.h index 682a5eff3..d947b87d5 100644 --- a/plugins/webpresence/webpresenceplugin.h +++ b/plugins/webpresence/webpresenceplugin.h @@ -1,126 +1,131 @@ /* webpresenceplugin.h Kopete Web Presence plugin Copyright (c) 2002,2003 by Will Stephenson Kopete (c) 2002,2003 by the Kopete developers ************************************************************************* * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ************************************************************************* */ #ifndef WEBPRESENCEPLUGIN_H #define WEBPRESENCEPLUGIN_H #include #include +#include "kopetecontact.h" #include "kopeteplugin.h" class QTimer; class QTemporaryFile; class KJob; namespace Kopete { } namespace Kopete { class Protocol; } namespace Kopete { class Account; } namespace Kopete { class OnlineStatus; } typedef QList ProtocolList; class WebPresencePlugin : public Kopete::Plugin { Q_OBJECT private: QUrl userStyleSheet; // Is set to true when Kopete has notified us // that we're about to be unloaded. bool shuttingDown; enum { WEB_HTML, WEB_XHTML, WEB_XML, WEB_CUSTOM, WEB_UNDEFINED } resultFormatting; public: WebPresencePlugin(QObject *parent, const QVariantList &args); virtual ~WebPresencePlugin(); void aboutToUnload() Q_DECL_OVERRIDE; protected Q_SLOTS: /** * Called when settings were changed */ void slotSettingsChanged(); /** * Write a file to the specified location, */ void slotWriteFile(); /** * Called when an upload finished, displays error if needed */ void slotUploadJobResult(KJob *); + /** + * This slot is used for normalising the signal recieved + */ + void slotRecieveStatusUpdate(Kopete::Contact *, const Kopete::OnlineStatus&, const Kopete::OnlineStatus&); /** * Called to schedule a write, after waiting to see if more changes * occur (accounts tend to change status together) */ void slotWaitMoreStatusChanges(); /** * Sets us up to respond to account status changes */ void listenToAllAccounts(); /** * Sets us up to respond to a new account */ void listenToAccount(Kopete::Account *account); protected: /** * Generate the file (HTML, text) to be uploaded */ QTemporaryFile *generateFile(); /** * Apply named stylesheet to get content and presentation */ bool transform(QTemporaryFile *src, QTemporaryFile *dest); /** * Helper method, generates list of all IM protocols */ ProtocolList allProtocols(); /** * Converts numeric status to a string */ QString statusAsString(const Kopete::OnlineStatus &newStatus); /** * Schedules writes */ QTimer *m_writeScheduler; // The file to be uploaded to the WWW QTemporaryFile *m_output; }; #endif // vim: set noet ts=4 sts=4 sw=4: