From bb447c91c138f25a4fd4c3df2145baf4a9e62c24 Mon Sep 17 00:00:00 2001 From: Patrick von Reth Date: Tue, 23 Jun 2015 17:04:57 +0200 Subject: [PATCH] add better support for markup in messages. added a test class --- CMakeLists.txt | 1 + autotest/CMakeLists.txt | 4 + autotest/snorebenchmark.cpp | 76 +++++++++++++++++++ lang/CMakeLists.txt | 10 ++- src/libsnore/notification/notification.cpp | 8 +- src/libsnore/notification/notification.h | 15 ++-- src/libsnore/plugins/plugins.h | 13 ++-- src/libsnore/snore.cpp | 1 + src/libsnore/utils.cpp | 37 +++++++++ src/libsnore/utils.h | 39 ++++------ src/plugins/backends/growl/growlbackend.cpp | 6 +- src/plugins/backends/snarl/snarl.cpp | 8 +- .../backends/snoretoast/snoretoast.cpp | 4 +- .../backends/trayicon/trayiconnotifer.cpp | 2 +- .../frontends/snarlnetwork/snarlnetwork.cpp | 2 + src/plugins/secondary_backends/nma/nma.cpp | 6 +- .../secondary_backends/pushover/pushover.cpp | 10 ++- .../secondary_backends/toasty/toasty.cpp | 4 +- 18 files changed, 183 insertions(+), 63 deletions(-) create mode 100644 autotest/CMakeLists.txt create mode 100644 autotest/snorebenchmark.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index eb705b4..bf9747c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,6 +71,7 @@ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin) add_subdirectory(data) add_subdirectory(lang) add_subdirectory(src) +add_subdirectory(autotest) add_subdirectory(share) diff --git a/autotest/CMakeLists.txt b/autotest/CMakeLists.txt new file mode 100644 index 0000000..1c2c521 --- /dev/null +++ b/autotest/CMakeLists.txt @@ -0,0 +1,4 @@ +include(ECMAddTests) +find_package(Qt5Test) + +ecm_add_tests( snorebenchmark.cpp LINK_LIBRARIES Snore::Libsnore Qt5::Test) diff --git a/autotest/snorebenchmark.cpp b/autotest/snorebenchmark.cpp new file mode 100644 index 0000000..5023182 --- /dev/null +++ b/autotest/snorebenchmark.cpp @@ -0,0 +1,76 @@ +#include +#include + +#include +#include + +#include +#include + +#include + +using namespace Snore; + + +class SnoreBenchmark : public QObject{ + Q_OBJECT +public: + SnoreBenchmark(){ + SnoreCore::instance(); + } + + + QString htmlTestString = QString("Italic A
" + "Italic B
" + "Bold
" + "Underline
" + "Font
" + "Website
"); +private slots: +void benchmarkUtilsToHtml(); +void benchmarkUtilsToPlain(); + +private: + +/** + * old toPlaintext function from Utils. + * @param string A string to decode if needed. + * @return if the string was rhichtext or html encoded a decoded string, else the original string. + */ +static inline QString toPlainText(const QString &string) +{ + if (Qt::mightBeRichText(string)) { + return QTextDocumentFragment::fromHtml(string).toPlainText(); + } else { + return string; + } +} +}; + + +void SnoreBenchmark::benchmarkUtilsToHtml(){ + + + QCOMPARE(Utils::normaliseMarkup(htmlTestString, Utils::NO_MARKUP), toPlainText(htmlTestString)); + QCOMPARE(Utils::normaliseMarkup(htmlTestString, Utils::HREF), QString("Italic A\n" + "Italic B\n" + "Bold\n" + "Underline\n" + "Font\n" + "Website\n")); + QCOMPARE(Utils::normaliseMarkup(htmlTestString, Utils::HREF | Utils::BOLD | Utils::BREAK | + Utils::UNDERLINE | Utils::FONT | Utils::ITALIC), htmlTestString); + QBENCHMARK{ + Utils::normaliseMarkup(htmlTestString, Utils::NO_MARKUP); + } +} + +void SnoreBenchmark::benchmarkUtilsToPlain() +{ + QBENCHMARK{ + toPlainText(htmlTestString); + } +} +QTEST_MAIN(SnoreBenchmark) + +#include "snorebenchmark.moc" diff --git a/lang/CMakeLists.txt b/lang/CMakeLists.txt index e88ff54..3df1289 100644 --- a/lang/CMakeLists.txt +++ b/lang/CMakeLists.txt @@ -8,8 +8,14 @@ foreach( lang ${LIBSNORE_LANGS} ) endforeach() set( snore_i18n_content "${snore_i18n_content}\n\n" ) -file( WRITE ${CMAKE_BINARY_DIR}/lang/snore_i18n.qrc "${snore_i18n_content}" ) +file( WRITE ${CMAKE_BINARY_DIR}/lang/snore_i18n.qrc.in "${snore_i18n_content}" ) + +#mark as a dependencie +add_custom_command(OUTPUT snore_i18n.qrc + COMMAND ${CMAKE_COMMAND} + ARGS -E copy ${CMAKE_BINARY_DIR}/lang/snore_i18n.qrc.in ${CMAKE_BINARY_DIR}/lang/snore_i18n.qrc + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") qt5_add_translation(libsnore_lang ${TS_FILES}) -add_custom_target(libsnore_lang_translation ALL DEPENDS ${libsnore_lang}) +add_custom_target(libsnore_lang_translation ALL DEPENDS ${libsnore_lang} DEPENDS snore_i18n.qrc) diff --git a/src/libsnore/notification/notification.cpp b/src/libsnore/notification/notification.cpp index ba3023d..62fb98b 100644 --- a/src/libsnore/notification/notification.cpp +++ b/src/libsnore/notification/notification.cpp @@ -92,14 +92,14 @@ const Application &Notification::application() const return d->m_application; } -QString Notification::title() const +QString Notification::title(Utils::MARKUP_FLAGS flags) const { - return d->m_title; + return Utils::normaliseMarkup(d->m_title, flags); } -QString Notification::text() const +QString Notification::text(Utils::MARKUP_FLAGS flags) const { - return d->m_text; + return Utils::normaliseMarkup(d->m_text, flags); } const Alert &Notification::alert() const diff --git a/src/libsnore/notification/notification.h b/src/libsnore/notification/notification.h index fb7d2b0..3d4bfd3 100644 --- a/src/libsnore/notification/notification.h +++ b/src/libsnore/notification/notification.h @@ -23,6 +23,7 @@ #include "notificationaction.h" #include "libsnore/hint.h" #include "libsnore/application.h" +#include "libsnore/utils.h" #include @@ -45,7 +46,7 @@ public: /** * The reason why the Notification was closed. */ - enum CloseReason { + enum CloseReasons { /** * The default value, the notification was not closed. */ @@ -79,14 +80,13 @@ public: */ REPLACED = 4 }; - Q_DECLARE_FLAGS(CloseReasons, CloseReason) Q_ENUMS(CloseReasons) /** * The Priority for the Notification. * Some notification systems support this flag to filter notifications or indicate different prioritys by color. */ - enum Priority { + enum Prioritys { /** * Indicates a low priority. */ @@ -102,7 +102,6 @@ public: */ HIGH = +1 }; - Q_DECLARE_FLAGS(Prioritys, Priority) Notification(); /** @@ -170,13 +169,13 @@ public: * * @return the title */ - QString title() const; + QString title(Utils::MARKUP_FLAGS flags = Utils::NO_MARKUP) const; /** * * @return the text body */ - QString text() const; + QString text(Utils::MARKUP_FLAGS flags = Utils::NO_MARKUP) const; /** * @@ -279,10 +278,6 @@ private: } Q_DECLARE_METATYPE(Snore::Notification) -Q_DECLARE_OPERATORS_FOR_FLAGS(Snore::Notification::CloseReasons) - -Q_DECLARE_OPERATORS_FOR_FLAGS(Snore::Notification::Prioritys) - QDataStream &operator<< (QDataStream &stream, const Snore::Notification ¬i); SNORE_EXPORT QDebug operator<< (QDebug, const Snore::Notification::CloseReasons &); diff --git a/src/libsnore/plugins/plugins.h b/src/libsnore/plugins/plugins.h index 4eb7d89..2a653b1 100644 --- a/src/libsnore/plugins/plugins.h +++ b/src/libsnore/plugins/plugins.h @@ -33,13 +33,14 @@ class SNORE_EXPORT SnorePlugin : public QObject Q_OBJECT public: enum PluginType { - NONE = 0, - ALL = 0xFFFFF,//for loading plugins - BACKEND = 0x1, - SECONDARY_BACKEND = 0x2, - FRONTEND = 0x4, - PLUGIN = 0x8 + NONE = 0, + BACKEND = 1 << 0, + SECONDARY_BACKEND = 1 << 1, + FRONTEND = 1 << 2, + PLUGIN = 1 << 3, + ALL = ~0 }; + Q_DECLARE_FLAGS(PluginTypes, PluginType) Q_ENUMS(PluginType) diff --git a/src/libsnore/snore.cpp b/src/libsnore/snore.cpp index 5c3b88c..e285da1 100644 --- a/src/libsnore/snore.cpp +++ b/src/libsnore/snore.cpp @@ -72,6 +72,7 @@ void SnoreCore::loadPlugins(SnorePlugin::PluginTypes types) } Q_D(SnoreCore); setValue("PluginTypes", QVariant::fromValue(types), LOCAL_SETTING); + snoreDebug(SNORE_DEBUG) << "Loading plugin types:" << types; for (SnorePlugin::PluginTypes type : SnorePlugin::types()) { if (type != SnorePlugin::ALL && types & type) { for (PluginContainer *info : PluginContainer::pluginCache(type).values()) { diff --git a/src/libsnore/utils.cpp b/src/libsnore/utils.cpp index bc36a18..6f5bc98 100644 --- a/src/libsnore/utils.cpp +++ b/src/libsnore/utils.cpp @@ -25,6 +25,9 @@ #include #endif +#include +#include + using namespace Snore; Utils::Utils(QObject *parent): @@ -66,6 +69,40 @@ void Utils::raiseWindowToFront(qlonglong wid) #endif } + +#define HTML_REPLACE(STRING, PATTERN){\ + static QRegExp regexp(QLatin1String(PATTERN));\ + STRING = STRING.replace(regexp, QStringLiteral("\\1"));\ + }\ + + +QString Utils::normaliseMarkup(QString string, MARKUP_FLAGS tags) +{ + static QMutex mutex; + QMutexLocker lock(&mutex); + + if (~tags & Utils::BREAK) { + static QRegExp br("
"); + string = string.replace(br, "\n"); + } + if (~tags & Utils::HREF) { + HTML_REPLACE(string, "([^<]*)"); + } + if (~tags & Utils::ITALIC) { + HTML_REPLACE(string, "([^<]*)"); + } + if (~tags & Utils::BOLD) { + HTML_REPLACE(string, "([^<]*)"); + } + if (~tags & Utils::UNDERLINE) { + HTML_REPLACE(string, "([^<]*)"); + } + if (~tags & Utils::FONT) { + HTML_REPLACE(string, "([^<]*)"); + } + return string; +} + #ifdef Q_OS_WIN int Utils::attatchToActiveProcess() { diff --git a/src/libsnore/utils.h b/src/libsnore/utils.h index 94f00b1..e1c20f7 100644 --- a/src/libsnore/utils.h +++ b/src/libsnore/utils.h @@ -24,8 +24,6 @@ #include #include #include -#include -#include namespace Snore { @@ -33,6 +31,19 @@ class SNORE_EXPORT Utils : public QObject { Q_OBJECT public: + enum MARKUP_FLAG{ + NO_MARKUP = 0, + HREF = 1 << 0, + BREAK = 1 << 1, + BOLD = 1 << 2, + ITALIC = 1 << 3, + UNDERLINE = 1 << 4, + FONT = 1 << 5, + ALL_MARKUP = ~0 + }; + + Q_DECLARE_FLAGS(MARKUP_FLAGS,MARKUP_FLAG) + Utils(QObject *parent = nullptr); ~Utils(); @@ -49,33 +60,12 @@ public: */ Q_INVOKABLE static void raiseWindowToFront(qlonglong wid); - /** - * - * @param string A string to decode if needed. - * @return if the string was rhichtext or html encoded a decoded string, else the original string. - */ - static inline QString toPlainText(const QString &string) - { - if (Qt::mightBeRichText(string)) { - return QTextDocumentFragment::fromHtml(string).toPlainText(); - } else { - return string; - } - } - /** * * @param string A string to encode if needed. * @return if the string was rhichtext html encode string is returnd otherwise the original string. */ - static inline QString toHtml(const QString &string) - { - if (Qt::mightBeRichText(string)) { - return QTextDocumentFragment::fromHtml(string).toHtml("UTF-8"); - } else { - return string; - } - } + static QString normaliseMarkup(QString string, MARKUP_FLAGS tags); /** * Computes a md5 hash of the provided data. @@ -125,5 +115,6 @@ private: }; } +Q_DECLARE_OPERATORS_FOR_FLAGS(Snore::Utils::MARKUP_FLAGS) #endif // UTILS_H diff --git a/src/plugins/backends/growl/growlbackend.cpp b/src/plugins/backends/growl/growlbackend.cpp index 42e0ba9..cb8d779 100644 --- a/src/plugins/backends/growl/growlbackend.cpp +++ b/src/plugins/backends/growl/growlbackend.cpp @@ -111,11 +111,11 @@ void GrowlBackend::slotNotify(Notification notification) { Growl *growl = m_applications.value(notification.application().name()); QString alert = notification.alert().name(); - snoreDebug(SNORE_DEBUG) << "Notify Growl:" << notification.application() << alert << Utils::toPlainText(notification.title()); + snoreDebug(SNORE_DEBUG) << "Notify Growl:" << notification.application() << alert << notification.title(); GrowlNotificationData data(alert.toUtf8().constData(), notification.id(), - Utils::toPlainText(notification.title()).toUtf8().constData(), - Utils::toPlainText(notification.text()).toUtf8().constData()); + notification.title().toUtf8().constData(), + notification.text().toUtf8().constData()); if (notification.icon().isValid()) { data.setIcon(notification.icon().localUrl().toUtf8().constData()); diff --git a/src/plugins/backends/snarl/snarl.cpp b/src/plugins/backends/snarl/snarl.cpp index 3c5d9c7..5a0f18a 100644 --- a/src/plugins/backends/snarl/snarl.cpp +++ b/src/plugins/backends/snarl/snarl.cpp @@ -217,8 +217,8 @@ void SnarlBackend::slotNotify(Notification notification) if (!notification.isUpdate()) { id = snarlInterface->Notify(notification.alert().name().toUtf8().constData(), - Utils::toPlainText(notification.title()).toUtf8().constData(), - Utils::toPlainText(notification.text()).toUtf8().constData(), + notification.title().toUtf8().constData(), + notification.text().toUtf8().constData(), notification.timeout(), notification.icon().isLocalFile() ? notification.icon().localUrl().toUtf8().constData() : 0, !notification.icon().isLocalFile() ? Icon::dataFromImage(notification.icon().image()).toBase64().constData() : 0, @@ -234,8 +234,8 @@ void SnarlBackend::slotNotify(Notification notification) id = notification.old().hints().privateValue(this, "id").toUInt(); snarlInterface->Update(id, notification.alert().name().toUtf8().constData(), - Utils::toPlainText(notification.title()).toUtf8().constData(), - Utils::toPlainText(notification.text()).toUtf8().constData(), + notification.title().toUtf8().constData(), + notification.text().toUtf8().constData(), notification.timeout(), notification.icon().isLocalFile() ? notification.icon().localUrl().toUtf8().constData() : 0, !notification.icon().isLocalFile() ? Icon::dataFromImage(notification.icon().image()).toBase64().constData() : 0, diff --git a/src/plugins/backends/snoretoast/snoretoast.cpp b/src/plugins/backends/snoretoast/snoretoast.cpp index 9edefc3..4780486 100644 --- a/src/plugins/backends/snoretoast/snoretoast.cpp +++ b/src/plugins/backends/snoretoast/snoretoast.cpp @@ -43,9 +43,9 @@ void SnoreToast::slotNotify(Notification notification) QStringList arguements; arguements << "-t" - << Utils::toPlainText(notification.title()) + << notification.title() << "-m" - << Utils::toPlainText(notification.text()); + << notification.text(); if (notification.icon().isValid()) { arguements << "-p" << QDir::toNativeSeparators(notification.icon().localUrl()); diff --git a/src/plugins/backends/trayicon/trayiconnotifer.cpp b/src/plugins/backends/trayicon/trayiconnotifer.cpp index cf42325..11fb0a1 100644 --- a/src/plugins/backends/trayicon/trayiconnotifer.cpp +++ b/src/plugins/backends/trayicon/trayiconnotifer.cpp @@ -83,7 +83,7 @@ void TrayIconNotifer::displayNotification(QSystemTrayIcon *icon) m_currentlyDisplaying = true; Notification notification = m_notificationQue.takeFirst(); m_displayed = notification; - icon->showMessage(Utils::toPlainText(notification.title()), Utils::toPlainText(notification.text()), QSystemTrayIcon::NoIcon, notification.timeout() * 1000); + icon->showMessage(notification.title(), notification.text(), QSystemTrayIcon::NoIcon, notification.timeout() * 1000); slotNotificationDisplayed(notification); } diff --git a/src/plugins/frontends/snarlnetwork/snarlnetwork.cpp b/src/plugins/frontends/snarlnetwork/snarlnetwork.cpp index 0b61eba..be9d7ff 100644 --- a/src/plugins/frontends/snarlnetwork/snarlnetwork.cpp +++ b/src/plugins/frontends/snarlnetwork/snarlnetwork.cpp @@ -86,6 +86,8 @@ void SnarlNetworkFrontend::slotNotificationClosed(Snore::Notification notificati case Notification::DISMISSED: callback(notification, "SNP/1.1/302/Notification cancelled/"); break; + default: + snoreDebug(SNORE_WARNING) << "Unhandled close reason" << notification.closeReason(); } } } diff --git a/src/plugins/secondary_backends/nma/nma.cpp b/src/plugins/secondary_backends/nma/nma.cpp index fd9f7bc..0e87dca 100644 --- a/src/plugins/secondary_backends/nma/nma.cpp +++ b/src/plugins/secondary_backends/nma/nma.cpp @@ -51,10 +51,12 @@ void NotifyMyAndroid::slotNotify(Notification notification) QString data(QString("apikey=%1&application=%2&event=%3&description=%4&priority=%5&content-type=text/html") .arg(key, notification.application().name(), - Utils::toPlainText(notification.title()), - Utils::toPlainText(notification.text()), + notification.title(), + notification.text(Utils::HREF | Utils::BOLD | Utils::BREAK | + Utils::UNDERLINE | Utils::FONT | Utils::ITALIC), QString::number(notification.priority()))); + QNetworkReply *reply = m_manager.post(request, data.toUtf8().constData()); connect(reply, &QNetworkReply::finished, [reply]() { snoreDebug(SNORE_DEBUG) << reply->error(); diff --git a/src/plugins/secondary_backends/pushover/pushover.cpp b/src/plugins/secondary_backends/pushover/pushover.cpp index ad03f77..36f9db2 100644 --- a/src/plugins/secondary_backends/pushover/pushover.cpp +++ b/src/plugins/secondary_backends/pushover/pushover.cpp @@ -52,15 +52,15 @@ void Pushover::slotNotify(Notification notification) QHttpPart title; title.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"title\"")); - title.setBody(Utils::toPlainText(notification.title()).toUtf8().constData()); + title.setBody(notification.title().toUtf8().constData()); mp->append(title); QHttpPart text; text.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"message\"")); - text.setBody(Utils::toPlainText(notification.text()).toUtf8().constData()); + text.setBody(notification.text(Utils::HREF | Utils::BOLD | Utils::UNDERLINE | Utils::FONT | Utils::ITALIC) + .toUtf8().constData()); mp->append(text); - QHttpPart priority; priority.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"priority\"")); priority.setBody(QString::number(notification.priority()).toUtf8().constData()); @@ -92,6 +92,10 @@ void Pushover::slotNotify(Notification notification) user.setBody(key.toUtf8().constData()); mp->append(user); + QHttpPart html; + html.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"html\"")); + html.setBody("1"); + mp->append(html); QNetworkReply *reply = m_manager.post(request, mp); mp->setParent(reply); diff --git a/src/plugins/secondary_backends/toasty/toasty.cpp b/src/plugins/secondary_backends/toasty/toasty.cpp index 2f3f90c..b9c9f04 100644 --- a/src/plugins/secondary_backends/toasty/toasty.cpp +++ b/src/plugins/secondary_backends/toasty/toasty.cpp @@ -49,12 +49,12 @@ void Toasty::slotNotify(Notification notification) QHttpPart title; title.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"title\"")); - title.setBody(Utils::toPlainText(notification.title()).toUtf8().constData()); + title.setBody(notification.title().toUtf8().constData()); mp->append(title); QHttpPart text; text.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"text\"")); - text.setBody(Utils::toPlainText(notification.text()).toUtf8().constData()); + text.setBody(notification.text().toUtf8().constData()); mp->append(text); QHttpPart app;