diff --git a/messageviewer/src/header/autotests/data/bcctest.tmpl b/messageviewer/src/header/autotests/data/bcctest.tmpl index a2d107f1..f003675c 100644 --- a/messageviewer/src/header/autotests/data/bcctest.tmpl +++ b/messageviewer/src/header/autotests/data/bcctest.tmpl @@ -1,7 +1,7 @@ -
nameOnly: {{ header.bccNameOnly|safe }}
+
nameOnly: {{ header.bcc.nameOnly|safe }}
isSet: {{ header.bcc.isSet|safe }}
-
fullAddress: {{ header.bcc|safe }}
-
str: {{ header.bccStr }}
-
expandable: {{ header.bccExpandableCc|safe }}
-
expandableTO: {{ header.bcc.expandableCC|safe }}
+
fullAddress: {{ header.bcc.fullAddress|safe }}
+
str: {{ header.bcc.str }}
+
expandable: {{ header.bcc.expandableBc|safe }}
+
expandableTO: {{ header.bcc.expandableBCC|safe }}
invalid: {{ header.bcc.invalid|safe }}
diff --git a/messageviewer/src/header/autotests/data/bcctest.tmpl.html b/messageviewer/src/header/autotests/data/bcctest.tmpl.html index 316a4ff8..50346d2b 100644 --- a/messageviewer/src/header/autotests/data/bcctest.tmpl.html +++ b/messageviewer/src/header/autotests/data/bcctest.tmpl.html @@ -1,17 +1,17 @@
nameOnly: blind copy recipient1, blind copy recipient2, blind copy recipient3, blind copy recipient4, blind copy recipient5, blind copy recipient6
-
isSet:
+
isSet: true
fullAddress: blind copy recipient1 <bccspam1@example.org>, blind copy recipient2 <bccspam2@example.org>, blind copy recipient3 <bccspam3@example.org>, blind copy recipient4 <bccspam4@example.org>, blind copy recipient5 <bccspam5@example.org>, blind copy recipient6 <bccspam6@example.org>
str: blind copy recipient1 <bccspam1@example.org>, blind copy recipient2 <bccspam2@example.org>, blind copy recipient3 <bccspam3@example.org>, blind copy recipient4 <bccspam4@example.org>, blind copy recipient5 <bccspam5@example.org>, blind copy recipient6 <bccspam6@example.org>
-
expandable:
-
expandableTO:
+
expandable: blind copy recipient1 <bccspam1@example.org>, blind copy recipient2 <bccspam2@example.org>, blind copy recipient3 <bccspam3@example.org>, blind copy recipient4 <bccspam4@example.org>blind copy recipient1 <bccspam1@example.org>, blind copy recipient2 <bccspam2@example.org>, blind copy recipient3 <bccspam3@example.org>, blind copy recipient4 <bccspam4@example.org>, blind copy recipient5 <bccspam5@example.org>, blind copy recipient6 <bccspam6@example.org>
+
expandableTO: blind copy recipient1 <bccspam1@example.org>, blind copy recipient2 <bccspam2@example.org>, blind copy recipient3 <bccspam3@example.org>, blind copy recipient4 <bccspam4@example.org>blind copy recipient1 <bccspam1@example.org>, blind copy recipient2 <bccspam2@example.org>, blind copy recipient3 <bccspam3@example.org>, blind copy recipient4 <bccspam4@example.org>, blind copy recipient5 <bccspam5@example.org>, blind copy recipient6 <bccspam6@example.org>
invalid:
diff --git a/messageviewer/src/header/autotests/data/cctest.tmpl b/messageviewer/src/header/autotests/data/cctest.tmpl index 990fd840..417b92b6 100644 --- a/messageviewer/src/header/autotests/data/cctest.tmpl +++ b/messageviewer/src/header/autotests/data/cctest.tmpl @@ -1,7 +1,7 @@ -
nameOnly: {{ header.ccNameOnly|safe }}
+
nameOnly: {{ header.cc.nameOnly|safe }}
isSet: {{ header.cc.isSet|safe }}
-
fullAddress: {{ header.cc|safe }}
-
str: {{ header.ccStr }}
-
expandable: {{ header.ccExpandable|safe }}
-
expandableTO: {{ header.cc.expandableTO|safe }}
+
fullAddress: {{ header.cc.fullAddress|safe }}
+
str: {{ header.cc.str }}
+
expandable: {{ header.cc.expandableCc|safe }}
+
expandableTO: {{ header.cc.expandableCC|safe }}
invalid: {{ header.cc.invalid|safe }}
diff --git a/messageviewer/src/header/autotests/data/cctest.tmpl.html b/messageviewer/src/header/autotests/data/cctest.tmpl.html index 4e48ca46..b08cdcf3 100644 --- a/messageviewer/src/header/autotests/data/cctest.tmpl.html +++ b/messageviewer/src/header/autotests/data/cctest.tmpl.html @@ -1,17 +1,17 @@
nameOnly: copy recipient1, copy recipient2, copy recipient3, copy recipient4, copy recipient5, copy recipient6
-
isSet:
+
isSet: true
fullAddress: copy recipient1 <ccspam1@example.org>, copy recipient2 <ccspam2@example.org>, copy recipient3 <ccspam3@example.org>, copy recipient4 <ccspam4@example.org>, copy recipient5 <ccspam5@example.org>, copy recipient6 <ccspam6@example.org>
str: copy recipient1 <ccspam1@example.org>, copy recipient2 <ccspam2@example.org>, copy recipient3 <ccspam3@example.org>, copy recipient4 <ccspam4@example.org>, copy recipient5 <ccspam5@example.org>, copy recipient6 <ccspam6@example.org>
expandable: copy recipient1 <ccspam1@example.org>, copy recipient2 <ccspam2@example.org>, copy recipient3 <ccspam3@example.org>, copy recipient4 <ccspam4@example.org>copy recipient1 <ccspam1@example.org>, copy recipient2 <ccspam2@example.org>, copy recipient3 <ccspam3@example.org>, copy recipient4 <ccspam4@example.org>, copy recipient5 <ccspam5@example.org>, copy recipient6 <ccspam6@example.org>
-
expandableTO:
+
expandableTO: copy recipient1 <ccspam1@example.org>, copy recipient2 <ccspam2@example.org>, copy recipient3 <ccspam3@example.org>, copy recipient4 <ccspam4@example.org>copy recipient1 <ccspam1@example.org>, copy recipient2 <ccspam2@example.org>, copy recipient3 <ccspam3@example.org>, copy recipient4 <ccspam4@example.org>, copy recipient5 <ccspam5@example.org>, copy recipient6 <ccspam6@example.org>
invalid:
diff --git a/messageviewer/src/header/autotests/data/datetest.tmpl b/messageviewer/src/header/autotests/data/datetest.tmpl index f7b1113f..1cbb6756 100644 --- a/messageviewer/src/header/autotests/data/datetest.tmpl +++ b/messageviewer/src/header/autotests/data/datetest.tmpl @@ -1,7 +1,7 @@ -str: {{ header.date|safe }} -short: {{ header.dateshort|safe }} -long: {{ header.datelong|safe }} -fancylong: {{ header.datefancylong|safe }} -fancyshort: {{ header.datefancyshort|safe }} -localelong: {{ header.datelocalelong|safe }} +str: {{ header.date.str|safe }} +short: {{ header.date.short|safe }} +long: {{ header.date.long|safe }} +fancylong: {{ header.date.fancylong|safe }} +fancyshort: {{ header.date.fancyshort|safe }} +localelong: {{ header.date.localelong|safe }} invalid: {{ header.date.invalid|safe }} diff --git a/messageviewer/src/header/autotests/data/fromtest.tmpl b/messageviewer/src/header/autotests/data/fromtest.tmpl index a579b89d..c730c382 100644 --- a/messageviewer/src/header/autotests/data/fromtest.tmpl +++ b/messageviewer/src/header/autotests/data/fromtest.tmpl @@ -1,7 +1,7 @@ -
nameOnly: {{ header.fromNameOnly|safe }}
+
nameOnly: {{ header.from.nameOnly|safe }}
isSet: {{ header.from.isSet|safe }}
-
fullAddress: {{ header.from|safe }}
-
str: {{ header.fromStr }}
-
expandable: {{ header.fromExpandable|safe }}
+
fullAddress: {{ header.from.fullAddress|safe }}
+
str: {{ header.from.str }}
+
expandable: {{ header.from.expandableFrom|safe }}
expandableTO: {{ header.fromexpandableTO|safe }}
invalid: {{ header.from.invalid|safe }}
diff --git a/messageviewer/src/header/autotests/data/fromtest.tmpl.html b/messageviewer/src/header/autotests/data/fromtest.tmpl.html index 7540fc2e..82cfe255 100644 --- a/messageviewer/src/header/autotests/data/fromtest.tmpl.html +++ b/messageviewer/src/header/autotests/data/fromtest.tmpl.html @@ -1,17 +1,17 @@
-
nameOnly:
-
isSet:
+
nameOnly: admin, from2, from3, from4, from5, from6
+
isSet: true
fullAddress: admin <admin@ssrr.link>, from2 <from2@example.org>, from3 <from3@example.org>, from4 <from4@example.org>, from5 <from5@example.org>, from6 <from6@example.org>
str: admin <admin@ssrr.link>, from2 <from2@example.org>, from3 <from3@example.org>, from4 <from4@example.org>, from5 <from5@example.org>, from6 <from6@example.org>
-
expandable:
+
expandable: admin <admin@ssrr.link>, from2 <from2@example.org>, from3 <from3@example.org>, from4 <from4@example.org>admin <admin@ssrr.link>, from2 <from2@example.org>, from3 <from3@example.org>, from4 <from4@example.org>, from5 <from5@example.org>, from6 <from6@example.org>
expandableTO:
invalid:
diff --git a/messageviewer/src/header/autotests/data/replyTotest.tmpl b/messageviewer/src/header/autotests/data/replyTotest.tmpl index eb2e325b..105a54a2 100644 --- a/messageviewer/src/header/autotests/data/replyTotest.tmpl +++ b/messageviewer/src/header/autotests/data/replyTotest.tmpl @@ -1,7 +1,7 @@ -
nameOnly: {{ header.replyToNameOnly|safe }}
+
nameOnly: {{ header.replyTo.nameOnly|safe }}
isSet: {{ header.replyTo.isSet|safe }}
-
fullAddress: {{ header.replyTo|safe }}
-
str: {{ header.replyToStr }}
-
expandable: {{ header.replyToExpandable|safe }}
-
expandableTO: {{ header.replyToexpandableTO|safe }}
+
fullAddress: {{ header.replyTo.fullAddress|safe }}
+
str: {{ header.replyTo.str }}
+
expandable: {{ header.replyTo.expandable|safe }}
+
expandableTO: {{ header.replyTo.expandableTO|safe }}
invalid: {{ header.replyTo.invalid|safe }}
diff --git a/messageviewer/src/header/autotests/data/replyTotest.tmpl.html b/messageviewer/src/header/autotests/data/replyTotest.tmpl.html index d10b9c6a..62b24c17 100644 --- a/messageviewer/src/header/autotests/data/replyTotest.tmpl.html +++ b/messageviewer/src/header/autotests/data/replyTotest.tmpl.html @@ -1,17 +1,17 @@
nameOnly: me, me3, me3, me4, me5, me6
-
isSet:
+
isSet: true
fullAddress: me <reply-to@example.org>, me3 <reply-to3@example.org>, me3 <reply-to3@example.org>, me4 <reply-to4@example.org>, me5 <reply-to5@example.org>, me6 <reply-to6@example.org>
str: me <reply-to@example.org>, me3 <reply-to3@example.org>, me3 <reply-to3@example.org>, me4 <reply-to4@example.org>, me5 <reply-to5@example.org>, me6 <reply-to6@example.org>
-
expandable:
-
expandableTO:
+
expandable: me <reply-to@example.org>, me3 <reply-to3@example.org>, me3 <reply-to3@example.org>, me4 <reply-to4@example.org>me <reply-to@example.org>, me3 <reply-to3@example.org>, me3 <reply-to3@example.org>, me4 <reply-to4@example.org>, me5 <reply-to5@example.org>, me6 <reply-to6@example.org>
+
expandableTO: me <reply-to@example.org>, me3 <reply-to3@example.org>, me3 <reply-to3@example.org>, me4 <reply-to4@example.org>me <reply-to@example.org>, me3 <reply-to3@example.org>, me3 <reply-to3@example.org>, me4 <reply-to4@example.org>, me5 <reply-to5@example.org>, me6 <reply-to6@example.org>
invalid:
diff --git a/messageviewer/src/header/autotests/data/resentfromtest.tmpl b/messageviewer/src/header/autotests/data/resentfromtest.tmpl index 7fc2f22b..adc29419 100644 --- a/messageviewer/src/header/autotests/data/resentfromtest.tmpl +++ b/messageviewer/src/header/autotests/data/resentfromtest.tmpl @@ -1,7 +1,7 @@ -
nameOnly: {{ header.resentfromNameOnly|safe }}
+
nameOnly: {{ header.resentfrom.nameOnly|safe }}
isSet: {{ header.resentfrom.isSet|safe }}
-
fullAddress: {{ header.resentfrom|safe }}
-
str: {{ header.resentfromStr }}
-
expandable: {{ header.resentfromExpandable|safe }}
-
expandableTO: {{ header.resentfromexpandableTO|safe }}
+
fullAddress: {{ header.resentfrom.fullAddress|safe }}
+
str: {{ header.resentfrom.str }}
+
expandable: {{ header.resentfrom.expandableResentFrom|safe }}
+
expandableTO: {{ header.resentfrom.expandableTO|safe }}
invalid: {{ header.resentfrom.invalid|safe }}
diff --git a/messageviewer/src/header/autotests/data/resentfromtest.tmpl.html b/messageviewer/src/header/autotests/data/resentfromtest.tmpl.html index 14c61926..ce60dc84 100644 --- a/messageviewer/src/header/autotests/data/resentfromtest.tmpl.html +++ b/messageviewer/src/header/autotests/data/resentfromtest.tmpl.html @@ -1,17 +1,17 @@
-
nameOnly:
-
isSet:
+
nameOnly: resent-from@example.org, resent-from2@example.org, resent-from3@example.org, resent-from4@example.org, resent-to5@example.org, resent-to6@example.org
+
isSet: true
fullAddress: resent-from@example.org, resent-from2@example.org, resent-from3@example.org, resent-from4@example.org, resent-to5@example.org, resent-to6@example.org
-
str:
-
expandable:
-
expandableTO:
+
str: resent-from@example.org, resent-from2@example.org, resent-from3@example.org, resent-from4@example.org, resent-to5@example.org, resent-to6@example.org
+
expandable: resent-from@example.org, resent-from2@example.org, resent-from3@example.org, resent-from4@example.orgresent-from@example.org, resent-from2@example.org, resent-from3@example.org, resent-from4@example.org, resent-to5@example.org, resent-to6@example.org
+
expandableTO: resent-from@example.org, resent-from2@example.org, resent-from3@example.org, resent-from4@example.orgresent-from@example.org, resent-from2@example.org, resent-from3@example.org, resent-from4@example.org, resent-to5@example.org, resent-to6@example.org
invalid:
diff --git a/messageviewer/src/header/autotests/data/resenttotest.tmpl b/messageviewer/src/header/autotests/data/resenttotest.tmpl index 4e9bc6e2..6786a71a 100644 --- a/messageviewer/src/header/autotests/data/resenttotest.tmpl +++ b/messageviewer/src/header/autotests/data/resenttotest.tmpl @@ -1,7 +1,7 @@ -
nameOnly: {{ header.resenttoNameOnly|safe }}
+
nameOnly: {{ header.resentto.nameOnly|safe }}
isSet: {{ header.resentto.isSet|safe }}
-
fullAddress: {{ header.resentto|safe }}
-
str: {{ header.resenttoStr }}
-
expandable: {{ header.resenttoExpandable|safe }}
-
expandableTO: {{ header.resenttoexpandableTO|safe }}
+
fullAddress: {{ header.resentto.fullAddress|safe }}
+
str: {{ header.resentto.str }}
+
expandable: {{ header.resentto.expandableResentto|safe }}
+
expandableTO: {{ header.resentto.expandableTO|safe }}
invalid: {{ header.resentto.invalid|safe }}
diff --git a/messageviewer/src/header/autotests/data/resenttotest.tmpl.html b/messageviewer/src/header/autotests/data/resenttotest.tmpl.html index 4d2c8d8c..0816fad4 100644 --- a/messageviewer/src/header/autotests/data/resenttotest.tmpl.html +++ b/messageviewer/src/header/autotests/data/resenttotest.tmpl.html @@ -1,17 +1,17 @@
-
nameOnly:
-
isSet:
+
nameOnly: resent-to@example.org, resent-to2@example.org, resent-to3@example.org, resent-to4@example.org, resent-to5@example.org, resent-to6@example.org
+
isSet: true
fullAddress: resent-to@example.org, resent-to2@example.org, resent-to3@example.org, resent-to4@example.org, resent-to5@example.org, resent-to6@example.org
-
str:
-
expandable:
-
expandableTO:
+
str: resent-to@example.org, resent-to2@example.org, resent-to3@example.org, resent-to4@example.org, resent-to5@example.org, resent-to6@example.org
+
expandable: resent-to@example.org, resent-to2@example.org, resent-to3@example.org, resent-to4@example.orgresent-to@example.org, resent-to2@example.org, resent-to3@example.org, resent-to4@example.org, resent-to5@example.org, resent-to6@example.org
+
expandableTO: resent-to@example.org, resent-to2@example.org, resent-to3@example.org, resent-to4@example.orgresent-to@example.org, resent-to2@example.org, resent-to3@example.org, resent-to4@example.org, resent-to5@example.org, resent-to6@example.org
invalid:
diff --git a/messageviewer/src/header/autotests/data/totest.tmpl b/messageviewer/src/header/autotests/data/totest.tmpl index 329f4fb7..30f41389 100644 --- a/messageviewer/src/header/autotests/data/totest.tmpl +++ b/messageviewer/src/header/autotests/data/totest.tmpl @@ -1,7 +1,7 @@ -
nameOnly: {{ header.toNameOnly|safe }}
+
nameOnly: {{ header.to.nameOnly|safe }}
isSet: {{ header.to.isSet|safe }}
-
fullAddress: {{ header.to|safe }}
-
str: {{ header.toStr}}
-
expandable: {{ header.toExpandable|safe }}
+
fullAddress: {{ header.to.fullAddress|safe }}
+
str: {{ header.to.str}}
+
expandable: {{ header.to.expandableTo|safe }}
expandableTO: {{ header.to.expandableTO|safe }}
invalid: {{ header.to.invalid|safe }}
diff --git a/messageviewer/src/header/autotests/data/totest.tmpl.html b/messageviewer/src/header/autotests/data/totest.tmpl.html index 8f2f32a6..ba617a99 100644 --- a/messageviewer/src/header/autotests/data/totest.tmpl.html +++ b/messageviewer/src/header/autotests/data/totest.tmpl.html @@ -1,17 +1,17 @@
nameOnly: direct recipient1, direct recipient2, direct recipient3, direct recipient4, direct recipient5, direct recipient6
-
isSet:
+
isSet: true
fullAddress: direct recipient1 <tospam1@example.org>, direct recipient2 <tospam2@example.org>, direct recipient3 <tospam3@example.org>, direct recipient4 <tospam4@example.org>, direct recipient5 <tospam5@example.org>, direct recipient6 <tospam6@example.org>
str: direct recipient1 <tospam1@example.org>, direct recipient2 <tospam2@example.org>, direct recipient3 <tospam3@example.org>, direct recipient4 <tospam4@example.org>, direct recipient5 <tospam5@example.org>, direct recipient6 <tospam6@example.org>
expandable: direct recipient1 <tospam1@example.org>, direct recipient2 <tospam2@example.org>, direct recipient3 <tospam3@example.org>, direct recipient4 <tospam4@example.org>direct recipient1 <tospam1@example.org>, direct recipient2 <tospam2@example.org>, direct recipient3 <tospam3@example.org>, direct recipient4 <tospam4@example.org>, direct recipient5 <tospam5@example.org>, direct recipient6 <tospam6@example.org>
-
expandableTO:
+
expandableTO: direct recipient1 <tospam1@example.org>, direct recipient2 <tospam2@example.org>, direct recipient3 <tospam3@example.org>, direct recipient4 <tospam4@example.org>direct recipient1 <tospam1@example.org>, direct recipient2 <tospam2@example.org>, direct recipient3 <tospam3@example.org>, direct recipient4 <tospam4@example.org>, direct recipient5 <tospam5@example.org>, direct recipient6 <tospam6@example.org>
invalid:
diff --git a/messageviewer/src/header/grantleeheaderformatter.cpp b/messageviewer/src/header/grantleeheaderformatter.cpp index 5d69582d..76669e6d 100644 --- a/messageviewer/src/header/grantleeheaderformatter.cpp +++ b/messageviewer/src/header/grantleeheaderformatter.cpp @@ -1,370 +1,402 @@ /* Copyright (C) 2013-2018 Laurent Montel This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "grantleeheaderformatter.h" #include "headerstyle_util.h" #include "grantleetheme/grantleetheme.h" #include "settings/messageviewersettings.h" #include "utils/iconnamecache.h" #include "config-messageviewer.h" #include #include #include #include #include #include -#include #include +#include +#include using namespace MessageCore; using namespace MessageViewer; +Q_DECLARE_METATYPE(const KMime::Headers::Generics::AddressList *) +Q_DECLARE_METATYPE(const KMime::Headers::Generics::MailboxList *) +Q_DECLARE_METATYPE(QSharedPointer) +Q_DECLARE_METATYPE(const KMime::Headers::Date *) + +// Read-only introspection of KMime::Headers::Generics::AddressList object. +namespace Grantlee { +template<> +inline QVariant TypeAccessor::lookUp(const KMime::Headers::Generics::AddressList *const object, const QString &property) +{ + if (property == QStringLiteral("nameOnly")) { + return StringUtil::emailAddrAsAnchor(object, StringUtil::DisplayNameOnly); + } else if (property == QStringLiteral("isSet")) { + return !object->asUnicodeString().isEmpty(); + } else if (property == QStringLiteral("fullAddress")) { + return StringUtil::emailAddrAsAnchor(object, StringUtil::DisplayFullAddress); + } else if (property == QStringLiteral("str")) { + return object->asUnicodeString(); + } else if (property.startsWith(QStringLiteral("expandable"))) { + const auto &name = property.mid(10); + const QString val = MessageCore::StringUtil::emailAddrAsAnchor( + object, MessageCore::StringUtil::DisplayFullAddress, + QString(), MessageCore::StringUtil::ShowLink, + MessageCore::StringUtil::ExpandableAddresses, + QStringLiteral("Full") + name + QStringLiteral("AddressList")); + return val; + } + return QVariant(); +} +} + +// Read-only introspection of KMime::Headers::Generics::MailboxList object. +namespace Grantlee { +template<> +inline QVariant TypeAccessor::lookUp(const KMime::Headers::Generics::MailboxList *const object, const QString &property) +{ + if (property == QStringLiteral("nameOnly")) { + return StringUtil::emailAddrAsAnchor(object, StringUtil::DisplayNameOnly); + } else if (property == QStringLiteral("isSet")) { + return !object->asUnicodeString().isEmpty(); + } else if (property == QStringLiteral("fullAddress")) { + return StringUtil::emailAddrAsAnchor(object, StringUtil::DisplayFullAddress); + } else if (property == QStringLiteral("str")) { + return object->asUnicodeString(); + } else if (property.startsWith(QStringLiteral("expandable"))) { + const auto &name = property.mid(10); + const QString val = MessageCore::StringUtil::emailAddrAsAnchor( + object, MessageCore::StringUtil::DisplayFullAddress, + QString(), MessageCore::StringUtil::ShowLink, + MessageCore::StringUtil::ExpandableAddresses, + QStringLiteral("Full") + name + QStringLiteral("AddressList")); + return val; + } + return QVariant(); +} +} + +GRANTLEE_BEGIN_LOOKUP(QSharedPointer) + if (property == QStringLiteral("nameOnly")) { + return StringUtil::emailAddrAsAnchor(object.data(), StringUtil::DisplayNameOnly); + } else if (property == QStringLiteral("isSet")) { + return !object->asUnicodeString().isEmpty(); + } else if (property == QStringLiteral("fullAddress")) { + return StringUtil::emailAddrAsAnchor(object.data(), StringUtil::DisplayFullAddress); + } else if (property == QStringLiteral("str")) { + return object->asUnicodeString(); + } else if (property.startsWith(QStringLiteral("expandable"))) { + const auto &name = property.mid(10); + const QString val = MessageCore::StringUtil::emailAddrAsAnchor( + object.data(), MessageCore::StringUtil::DisplayFullAddress, + QString(), MessageCore::StringUtil::ShowLink, + MessageCore::StringUtil::ExpandableAddresses, + QStringLiteral("Full") + name + QStringLiteral("AddressList")); + return val; + } +GRANTLEE_END_LOOKUP + + +namespace Grantlee { +template<> +inline QVariant TypeAccessor::lookUp(const KMime::Headers::Date *const object, const QString &property) +{ + MessageViewer::HeaderStyleUtil::HeaderStyleUtilDateFormat dateFormat; + if (property == QStringLiteral("str")) { + return HeaderStyleUtil::dateStr(object->dateTime()); + } else if (property == QStringLiteral("short")) { + dateFormat = MessageViewer::HeaderStyleUtil::ShortDate; + } else if (property == QStringLiteral("long")) { + dateFormat = MessageViewer::HeaderStyleUtil::CustomDate; + } else if (property == QStringLiteral("fancylong")) { + dateFormat = MessageViewer::HeaderStyleUtil::FancyLongDate; + } else if (property == QStringLiteral("fancyshort")) { + dateFormat = MessageViewer::HeaderStyleUtil::FancyShortDate; + } else if(property == QStringLiteral("localelong")){ + dateFormat = MessageViewer::HeaderStyleUtil::LongDate; + } else { + return QVariant(); + } + + return HeaderStyleUtil::strToHtml(HeaderStyleUtil::dateString(object, dateFormat)); +} +} + + class Q_DECL_HIDDEN MessageViewer::GrantleeHeaderFormatter::Private { public: Private() { + Grantlee::registerMetaType(); + Grantlee::registerMetaType(); + Grantlee::registerMetaType>(); + Grantlee::registerMetaType(); iconSize = KIconLoader::global()->currentSize(KIconLoader::Toolbar); engine = new Grantlee::Engine; templateLoader = QSharedPointer( new Grantlee::FileSystemTemplateLoader); engine->addTemplateLoader(templateLoader); } ~Private() { delete engine; } QSharedPointer templateLoader; Grantlee::Engine *engine = nullptr; MessageViewer::HeaderStyleUtil headerStyleUtil; int iconSize; }; GrantleeHeaderFormatter::GrantleeHeaderFormatter() : d(new GrantleeHeaderFormatter::Private) { } GrantleeHeaderFormatter::~GrantleeHeaderFormatter() { delete d; } QString GrantleeHeaderFormatter::toHtml( const GrantleeHeaderFormatter::GrantleeHeaderFormatterSettings &settings) const { QString errorMessage; if (!settings.theme.isValid()) { errorMessage = i18n("Grantlee theme \"%1\" is not valid.", settings.theme.name()); return errorMessage; } d->templateLoader->setTemplateDirs(QStringList() << settings.theme.absolutePath()); Grantlee::Template headerTemplate = d->engine->loadByName(settings.theme.themeFilename()); if (headerTemplate->error()) { errorMessage = headerTemplate->errorString(); return errorMessage; } return format( settings.theme.absolutePath(), headerTemplate, settings.theme.displayExtraVariables(), settings.isPrinting, settings.style, settings.message, settings.showEmoticons); } QString GrantleeHeaderFormatter::toHtml(const QStringList &displayExtraHeaders, const QString &absolutPath, const QString &filename, const MessageViewer::HeaderStyle *style, KMime::Message *message, bool isPrinting) const { d->templateLoader->setTemplateDirs(QStringList() << absolutPath); Grantlee::Template headerTemplate = d->engine->loadByName(filename); if (headerTemplate->error()) { return headerTemplate->errorString(); } return format(absolutPath, headerTemplate, displayExtraHeaders, isPrinting, style, message); } QString GrantleeHeaderFormatter::format(const QString &absolutePath, const Grantlee::Template &headerTemplate, const QStringList &displayExtraHeaders, bool isPrinting, const MessageViewer::HeaderStyle *style, KMime::Message *message, bool showEmoticons) const { QVariantHash headerObject; // However, the direction of the message subject within the header is // determined according to the contents of the subject itself. Since // the "Re:" and "Fwd:" prefixes would always cause the subject to be // considered left-to-right, they are ignored when determining its // direction. const QString absoluteThemePath = QUrl::fromLocalFile(absolutePath + QLatin1Char('/')).url(); headerObject.insert(QStringLiteral("absoluteThemePath"), absoluteThemePath); headerObject.insert(QStringLiteral("applicationDir"), QApplication::isRightToLeft() ? QStringLiteral("rtl") : QStringLiteral( "ltr")); headerObject.insert(QStringLiteral("subjectDir"), d->headerStyleUtil.subjectDirectionString(message)); headerObject.insert(QStringLiteral("subjecti18n"), i18n("Subject:")); KTextToHTML::Options flags = KTextToHTML::PreserveSpaces; if (showEmoticons) { flags |= KTextToHTML::ReplaceSmileys; } headerObject.insert(QStringLiteral("subject"), d->headerStyleUtil.subjectString(message, flags)); if (message->to(false)) { headerObject.insert(QStringLiteral("toi18n"), i18n("To:")); - headerObject.insert(QStringLiteral("to"), - StringUtil::emailAddrAsAnchor(message->to(), - StringUtil::DisplayFullAddress)); - headerObject.insert(QStringLiteral("toNameOnly"), - StringUtil::emailAddrAsAnchor( - message->to(), StringUtil::DisplayNameOnly)); - headerObject.insert(QStringLiteral("toStr"), message->to()->asUnicodeString()); - const QString val = MessageCore::StringUtil::emailAddrAsAnchor( - message->to(), MessageCore::StringUtil::DisplayFullAddress, - QString(), MessageCore::StringUtil::ShowLink, - MessageCore::StringUtil::ExpandableAddresses, - QStringLiteral("FullToAddressList")); - headerObject.insert(QStringLiteral("toExpandable"), val); - headerObject.insert(QStringLiteral("toMailbox"), QVariant::fromValue(message->to())); + headerObject.insert(QStringLiteral("to"), QVariant::fromValue(static_cast(message->to()))); } if (message->replyTo(false)) { headerObject.insert(QStringLiteral("replyToi18n"), i18n("Reply to:")); - headerObject.insert(QStringLiteral("replyTo"), - StringUtil::emailAddrAsAnchor(message->replyTo(), - StringUtil::DisplayFullAddress)); - headerObject.insert(QStringLiteral("replyToStr"), message->replyTo()->asUnicodeString()); - headerObject.insert(QStringLiteral("replyToNameOnly"), - StringUtil::emailAddrAsAnchor(message->replyTo(), - StringUtil::DisplayNameOnly)); + headerObject.insert(QStringLiteral("replyTo"), QVariant::fromValue(static_cast(message->replyTo()))); } if (message->cc(false)) { headerObject.insert(QStringLiteral("cci18n"), i18n("CC:")); - headerObject.insert(QStringLiteral("cc"), - StringUtil::emailAddrAsAnchor(message->cc(), - StringUtil::DisplayFullAddress)); - headerObject.insert(QStringLiteral("ccStr"), message->cc()->asUnicodeString()); - headerObject.insert(QStringLiteral("ccNameOnly"), - StringUtil::emailAddrAsAnchor( - message->cc(), StringUtil::DisplayNameOnly)); - headerObject.insert(QStringLiteral("ccMailbox"), QVariant::fromValue(message->cc())); - const QString val = MessageCore::StringUtil::emailAddrAsAnchor( - message->cc(), MessageCore::StringUtil::DisplayFullAddress, - QString(), MessageCore::StringUtil::ShowLink, - MessageCore::StringUtil::ExpandableAddresses, - QStringLiteral("FullCcAddressList")); - headerObject.insert(QStringLiteral("ccExpandable"), val); + headerObject.insert(QStringLiteral("cc"), QVariant::fromValue(static_cast(message->cc()))); } if (message->bcc(false)) { headerObject.insert(QStringLiteral("bcci18n"), i18n("BCC:")); - headerObject.insert(QStringLiteral("bcc"), - StringUtil::emailAddrAsAnchor(message->bcc(), - StringUtil::DisplayFullAddress)); - headerObject.insert(QStringLiteral("bccNameOnly"), - StringUtil::emailAddrAsAnchor(message->bcc(), - StringUtil::DisplayNameOnly)); - headerObject.insert(QStringLiteral("bccStr"), message->bcc()->asUnicodeString()); - headerObject.insert(QStringLiteral("bccMailbox"), QVariant::fromValue(message->bcc())); + headerObject.insert(QStringLiteral("bcc"), QVariant::fromValue(static_cast(message->bcc()))); } headerObject.insert(QStringLiteral("fromi18n"), i18n("From:")); - headerObject.insert(QStringLiteral("from"), - StringUtil::emailAddrAsAnchor(message->from(), - StringUtil::DisplayFullAddress)); - headerObject.insert(QStringLiteral("fromStr"), message->from()->asUnicodeString()); + headerObject.insert(QStringLiteral("from"), QVariant::fromValue(static_cast(message->from()))); //Sender + headerObject.insert(QStringLiteral("senderi18n"), i18n("Sender:")); headerObject.insert(QStringLiteral("sender"), d->headerStyleUtil.strToHtml(message->sender()->asUnicodeString())); - headerObject.insert(QStringLiteral("senderi18n"), i18n("Sender:")); headerObject.insert(QStringLiteral("listidi18n"), i18n("List-Id:")); if (auto hrd = message->headerByType("List-Id")) { headerObject.insert(QStringLiteral("listid"), hrd->asUnicodeString()); } const QString spamHtml = d->headerStyleUtil.spamStatus(message); if (!spamHtml.isEmpty()) { headerObject.insert(QStringLiteral("spamstatusi18n"), i18n("Spam Status:")); headerObject.insert(QStringLiteral("spamHTML"), spamHtml); } - headerObject.insert(QStringLiteral("datei18n"), i18n("Date:")); - headerObject.insert(QStringLiteral("dateshort"), - d->headerStyleUtil.strToHtml(d->headerStyleUtil.dateString(message, - isPrinting, - MessageViewer:: - HeaderStyleUtil:: - ShortDate))); - headerObject.insert(QStringLiteral("datelong"), - d->headerStyleUtil.strToHtml(d->headerStyleUtil.dateString(message, - isPrinting, - MessageViewer:: - HeaderStyleUtil:: - CustomDate))); - headerObject.insert(QStringLiteral("date"), - d->headerStyleUtil.dateStr(message->date()->dateTime())); - headerObject.insert(QStringLiteral("datefancylong"), - d->headerStyleUtil.strToHtml(d->headerStyleUtil.dateString(message, - isPrinting, - MessageViewer:: - HeaderStyleUtil:: - FancyLongDate))); - headerObject.insert(QStringLiteral("datefancyshort"), - d->headerStyleUtil.strToHtml(d->headerStyleUtil.dateString(message, - isPrinting, - MessageViewer:: - HeaderStyleUtil:: - FancyShortDate))); - headerObject.insert(QStringLiteral("datelocalelong"), - d->headerStyleUtil.strToHtml(d->headerStyleUtil.dateString(message, - isPrinting, - MessageViewer:: - HeaderStyleUtil:: - LongDate))); + headerObject.insert(QStringLiteral("datei18n"), i18n("Date:")); + headerObject.insert(QStringLiteral("date"), QVariant::fromValue(static_cast(message->date()))); if (message->hasHeader("Resent-From")) { headerObject.insert(QStringLiteral("resentfromi18n"), i18n("resent from")); - const QVector resentFrom - = d->headerStyleUtil.resentFromList(message); headerObject.insert(QStringLiteral("resentfrom"), - StringUtil::emailAddrAsAnchor(resentFrom, - StringUtil::DisplayFullAddress)); + QVariant::fromValue(HeaderStyleUtil::resentFromList(message))); } if (message->hasHeader("Resent-To")) { - const QVector resentTo = d->headerStyleUtil.resentToList(message); + auto resentTo = HeaderStyleUtil::resentToList(message); headerObject.insert(QStringLiteral("resenttoi18n"), - i18np("receiver was", "receivers were", resentTo.count())); + i18np("receiver was", "receivers were", resentTo->mailboxes().count())); headerObject.insert(QStringLiteral("resentto"), - StringUtil::emailAddrAsAnchor(resentTo, - StringUtil::DisplayFullAddress)); + QVariant::fromValue(HeaderStyleUtil::resentToList(message))); } if (auto organization = message->organization(false)) { headerObject.insert(QStringLiteral("organization"), d->headerStyleUtil.strToHtml(organization->asUnicodeString())); } if (!style->vCardName().isEmpty()) { headerObject.insert(QStringLiteral("vcardname"), style->vCardName()); } if (!style->collectionName().isEmpty()) { headerObject.insert(QStringLiteral("collectionname"), style->collectionName()); } if (isPrinting) { //provide a bit more left padding when printing //kolab/issue3254 (printed mail cut at the left side) //Use it just for testing if we are in printing mode headerObject.insert(QStringLiteral("isprinting"), i18n("Printing mode")); headerObject.insert(QStringLiteral("printmode"), QStringLiteral("printmode")); } else { headerObject.insert(QStringLiteral("screenmode"), QStringLiteral("screenmode")); } // colors depend on if it is encapsulated or not QColor fontColor(Qt::white); QString linkColor = QStringLiteral("white"); const QColor activeColor = KColorScheme(QPalette::Active, KColorScheme::Selection).background().color(); QColor activeColorDark = activeColor.darker(130); // reverse colors for encapsulated if (!style->isTopLevel()) { activeColorDark = activeColor.darker(50); fontColor = QColor(Qt::black); linkColor = QStringLiteral("black"); } // 3D borders headerObject.insert(QStringLiteral("activecolordark"), activeColorDark.name()); headerObject.insert(QStringLiteral("fontcolor"), fontColor.name()); headerObject.insert(QStringLiteral("linkcolor"), linkColor); MessageViewer::HeaderStyleUtil::xfaceSettings xface = d->headerStyleUtil.xface(style, message); if (!xface.photoURL.isEmpty()) { headerObject.insert(QStringLiteral("photowidth"), xface.photoWidth); headerObject.insert(QStringLiteral("photoheight"), xface.photoHeight); headerObject.insert(QStringLiteral("photourl"), xface.photoURL); } for (QString header : qAsConst(displayExtraHeaders)) { const QByteArray baHeader = header.toLocal8Bit(); if (auto hrd = message->headerByType(baHeader.constData())) { //Grantlee doesn't support '-' in variable name => remove it. header = header.remove(QLatin1Char('-')); headerObject.insert(header, hrd->asUnicodeString()); } } headerObject.insert(QStringLiteral("vcardi18n"), i18n("[vcard]")); headerObject.insert(QStringLiteral("readOnlyMessage"), style->readOnlyMessage()); const bool messageHasAttachment = KMime::hasAttachment(message); headerObject.insert(QStringLiteral("hasAttachment"), messageHasAttachment); headerObject.insert(QStringLiteral("attachmentHtml"), style->attachmentHtml()); if (messageHasAttachment) { const QString iconPath = MessageViewer::IconNameCache::instance()->iconPath(QStringLiteral( "mail-attachment"), KIconLoader::Toolbar); const QString html = QStringLiteral("").arg(QUrl::fromLocalFile(iconPath).url(), QString::number(d->iconSize)); headerObject.insert(QStringLiteral("attachmentIcon"), html); } const bool messageIsSigned = KMime::isSigned(message); headerObject.insert(QStringLiteral("messageIsSigned"), messageIsSigned); if (messageIsSigned) { const QString iconPath = MessageViewer::IconNameCache::instance()->iconPath(QStringLiteral( "mail-signed"), KIconLoader::Toolbar); const QString html = QStringLiteral("").arg(QUrl::fromLocalFile(iconPath).url(), QString::number(d->iconSize)); headerObject.insert(QStringLiteral("signedIcon"), html); } const bool messageIsEncrypted = KMime::isEncrypted(message); headerObject.insert(QStringLiteral("messageIsEncrypted"), messageIsEncrypted); if (messageIsEncrypted) { const QString iconPath = MessageViewer::IconNameCache::instance()->iconPath(QStringLiteral( "mail-encrypted"), KIconLoader::Toolbar); const QString html = QStringLiteral("").arg(QUrl::fromLocalFile(iconPath).url(), QString::number(d->iconSize)); headerObject.insert(QStringLiteral("encryptedIcon"), html); } const bool messageHasSecurityInfo = messageIsEncrypted || messageIsSigned; headerObject.insert(QStringLiteral("messageHasSecurityInfo"), messageHasSecurityInfo); headerObject.insert(QStringLiteral("messageHasSecurityInfoI18n"), i18n("Security:")); QVariantHash mapping; mapping.insert(QStringLiteral("header"), headerObject); Grantlee::Context context(mapping); return headerTemplate->render(&context); } diff --git a/messageviewer/src/header/headerstyle_util.cpp b/messageviewer/src/header/headerstyle_util.cpp index b42c2dc3..56009fdd 100644 --- a/messageviewer/src/header/headerstyle_util.cpp +++ b/messageviewer/src/header/headerstyle_util.cpp @@ -1,413 +1,400 @@ /* Copyright (C) 2013-2018 Laurent Montel This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "headerstyle_util.h" #include "messageviewer_debug.h" #include "contactdisplaymessagememento.h" #include "kxface.h" #include "header/headerstyle.h" #include "utils/iconnamecache.h" #include "settings/messageviewersettings.h" #include #include #include #include #include #include #include #include #include using namespace MessageCore; using namespace MessageViewer; // // Convenience functions: // HeaderStyleUtil::HeaderStyleUtil() { } QString HeaderStyleUtil::directionOf(const QString &str) const { return str.isRightToLeft() ? QStringLiteral("rtl") : QStringLiteral("ltr"); } -QString HeaderStyleUtil::strToHtml(const QString &str, KTextToHTML::Options flags) const +QString HeaderStyleUtil::strToHtml(const QString &str, KTextToHTML::Options flags) { return KTextToHTML::convertToHtml(str, flags, 4096, 512); } -// Prepare the date string (when printing always use the localized date) -QString HeaderStyleUtil::dateString(KMime::Message *message, bool printing, HeaderStyleUtilDateFormat dateFormat) const +// Prepare the date string +QString HeaderStyleUtil::dateString(KMime::Message *message, HeaderStyleUtilDateFormat dateFormat) { - const QDateTime dateTime = message->date()->dateTime(); + return dateString(message->date(), dateFormat); +} + +QString HeaderStyleUtil::dateString(const KMime::Headers::Date *date, HeaderStyleUtilDateFormat dateFormat) +{ + const QDateTime dateTime = date->dateTime(); if (!dateTime.isValid()) { qCDebug(MESSAGEVIEWER_LOG) << "Unable to parse date"; return i18nc("Unknown date", "Unknown"); } - const time_t unixTime = dateTime.toSecsSinceEpoch(); - if (printing) { + const time_t unixTime = dateTime.toTime_t(); + switch (dateFormat) { + case ShortDate: return KMime::DateFormatter::formatDate(KMime::DateFormatter::Localized, unixTime); - } else { - switch (dateFormat) { - case ShortDate: - return KMime::DateFormatter::formatDate(KMime::DateFormatter::Localized, unixTime); - case LongDate: - return KMime::DateFormatter::formatDate(KMime::DateFormatter::CTime, unixTime); - case FancyShortDate: - return KMime::DateFormatter::formatDate(KMime::DateFormatter::Fancy, unixTime); - case FancyLongDate: - //Laurent fix me - //TODO return QLocale::system().toString(dateTime, QLocale::LongFormat); - case CustomDate: - default: - return dateStr(dateTime); - } + case LongDate: + return KMime::DateFormatter::formatDate(KMime::DateFormatter::CTime, unixTime); + case FancyShortDate: + return KMime::DateFormatter::formatDate(KMime::DateFormatter::Fancy, unixTime); + case FancyLongDate: + //Laurent fix me + //TODO return QLocale::system().toString(dateTime, QLocale::LongFormat); + case CustomDate: + default: + return dateStr(dateTime); } } QString HeaderStyleUtil::subjectString(KMime::Message *message, KTextToHTML::Options flags) const { QString subjectStr; const KMime::Headers::Subject *const subject = message->subject(false); if (subject) { subjectStr = subject->asUnicodeString(); if (subjectStr.isEmpty()) { subjectStr = i18n("No Subject"); } else { subjectStr = strToHtml(subjectStr, flags); } } else { subjectStr = i18n("No Subject"); } return subjectStr; } QString HeaderStyleUtil::subjectDirectionString(KMime::Message *message) const { QString subjectDir; if (message->subject(false)) { subjectDir = directionOf(MessageCore::StringUtil::cleanSubject(message)); } else { subjectDir = directionOf(i18n("No Subject")); } return subjectDir; } QString HeaderStyleUtil::spamStatus(KMime::Message *message) const { QString spamHTML; const SpamScores scores = SpamHeaderAnalyzer::getSpamScores(message); for (SpamScores::const_iterator it = scores.constBegin(), end = scores.constEnd(); it != end; ++it) { spamHTML += (*it).agent() + QLatin1Char(' ') +drawSpamMeter((*it).error(), (*it).score(), (*it).confidence(), (*it).spamHeader(), (*it).confidenceHeader()); } return spamHTML; } QString HeaderStyleUtil::drawSpamMeter(SpamError spamError, double percent, double confidence, const QString &filterHeader, const QString &confidenceHeader) const { static const int meterWidth = 20; static const int meterHeight = 5; QImage meterBar(meterWidth, 1, QImage::Format_Indexed8 /*QImage::Format_RGB32*/); meterBar.setColorCount(24); meterBar.setColor(meterWidth + 1, qRgb(255, 255, 255)); meterBar.setColor(meterWidth + 2, qRgb(170, 170, 170)); if (spamError != noError) { // grey is for errors meterBar.fill(meterWidth + 2); } else { static const unsigned short gradient[meterWidth][3] = { { 0, 255, 0 }, { 27, 254, 0 }, { 54, 252, 0 }, { 80, 250, 0 }, { 107, 249, 0 }, { 135, 247, 0 }, { 161, 246, 0 }, { 187, 244, 0 }, { 214, 242, 0 }, { 241, 241, 0 }, { 255, 228, 0 }, { 255, 202, 0 }, { 255, 177, 0 }, { 255, 151, 0 }, { 255, 126, 0 }, { 255, 101, 0 }, { 255, 76, 0 }, { 255, 51, 0 }, { 255, 25, 0 }, { 255, 0, 0 } }; meterBar.fill(meterWidth + 1); const int max = qMin(meterWidth, static_cast(percent) / 5); for (int i = 0; i < max; ++i) { meterBar.setColor(i + 1, qRgb(gradient[i][0], gradient[i][1], gradient[i][2])); meterBar.setPixel(i, 0, i + 1); } } QString titleText; QString confidenceString; if (spamError == noError) { if (confidence >= 0) { confidenceString = QString::number(confidence) + QLatin1String("%  "); titleText = i18n("%1% probability of being spam with confidence %3%.\n\n" "Full report:\nProbability=%2\nConfidence=%4", QString::number(percent, 'f', 2), filterHeader, confidence, confidenceHeader); } else { // do not show negative confidence confidenceString = QString() + QLatin1String(" "); titleText = i18n("%1% probability of being spam.\n\n" "Full report:\nProbability=%2", QString::number(percent, 'f', 2), filterHeader); } } else { QString errorMsg; switch (spamError) { case errorExtractingAgentString: errorMsg = i18n("No Spam agent"); break; case couldNotConverScoreToFloat: errorMsg = i18n("Spam filter score not a number"); break; case couldNotConvertThresholdToFloatOrThresholdIsNegative: errorMsg = i18n("Threshold not a valid number"); break; case couldNotFindTheScoreField: errorMsg = i18n("Spam filter score could not be extracted from header"); break; case couldNotFindTheThresholdField: errorMsg = i18n("Threshold could not be extracted from header"); break; default: errorMsg = i18n("Error evaluating spam score"); break; } // report the error in the spam filter titleText = i18n("%1.\n\n" "Full report:\n%2", errorMsg, filterHeader); } return QStringLiteral( "") .arg(imgToDataUrl(meterBar), QString::number(meterWidth), QString::number(meterHeight), titleText) + confidenceString; } QString HeaderStyleUtil::imgToDataUrl(const QImage &image) const { QByteArray ba; QBuffer buffer(&ba); buffer.open(QIODevice::WriteOnly); image.save(&buffer, "PNG"); return QStringLiteral("data:image/%1;base64,%2").arg(QStringLiteral("PNG"), QString::fromLatin1(ba.toBase64())); } -QString HeaderStyleUtil::dateStr(const QDateTime &dateTime) const +QString HeaderStyleUtil::dateStr(const QDateTime &dateTime) { const time_t unixTime = dateTime.toSecsSinceEpoch(); return KMime::DateFormatter::formatDate( static_cast( MessageCore::MessageCoreSettings::self()->dateFormat()), unixTime, MessageCore::MessageCoreSettings::self()->customDateFormat()); } -QString HeaderStyleUtil::dateShortStr(const QDateTime &dateTime) const +QString HeaderStyleUtil::dateShortStr(const QDateTime &dateTime) { KMime::DateFormatter formatter(KMime::DateFormatter::Fancy); return formatter.dateString(dateTime); } -QVector HeaderStyleUtil::resentFromList(KMime::Message *message) const +QSharedPointer mailboxesFromHeader(const KMime::Headers::Base *hrd) +{ + QSharedPointer mailboxList(new KMime::Headers::Generics::MailboxList()); + const QByteArray &data = hrd->as7BitString(false); + mailboxList->from7BitString(data); + return mailboxList; +} + +QSharedPointer HeaderStyleUtil::resentFromList(KMime::Message *message) { - // Get the resent-from header into a Mailbox - QVector resentFrom; if (auto hrd = message->headerByType("Resent-From")) { - const QByteArray data = hrd->as7BitString(false); - const char *start = data.data(); - const char *end = start + data.length(); - KMime::Types::AddressList addressList; - KMime::HeaderParsing::parseAddressList(start, end, addressList); - for (const KMime::Types::Address &addr : qAsConst(addressList)) { - for (const KMime::Types::Mailbox &mbox : qAsConst(addr.mailboxList)) { - resentFrom.append(mbox); - } - } + return mailboxesFromHeader(hrd); } - return resentFrom; + return nullptr; } -QVector HeaderStyleUtil::resentToList(KMime::Message *message) const +QSharedPointer HeaderStyleUtil::resentToList(KMime::Message *message) { - // Get the resent-from header into a Mailbox - QVector resentTo; if (auto hrd = message->headerByType("Resent-To")) { - const QByteArray data = hrd->as7BitString(false); - const char *start = data.data(); - const char *end = start + data.length(); - KMime::Types::AddressList addressList; - KMime::HeaderParsing::parseAddressList(start, end, addressList); - for (const KMime::Types::Address &addr : qAsConst(addressList)) { - for (const KMime::Types::Mailbox &mbox : qAsConst(addr.mailboxList)) { - resentTo.append(mbox); - } - } + return mailboxesFromHeader(hrd); } - return resentTo; + return nullptr; } void HeaderStyleUtil::updateXFaceSettings(QImage photo, xfaceSettings &settings) const { if (!photo.isNull()) { settings.photoWidth = photo.width(); settings.photoHeight = photo.height(); // scale below 60, otherwise it can get way too large if (settings.photoHeight > 60) { double ratio = (double)settings.photoHeight / (double)settings.photoWidth; settings.photoHeight = 60; settings.photoWidth = (int)(60 / ratio); photo = photo.scaled(settings.photoWidth, settings.photoHeight, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); } settings.photoURL = MessageViewer::HeaderStyleUtil::imgToDataUrl(photo); } } HeaderStyleUtil::xfaceSettings HeaderStyleUtil::xface(const MessageViewer::HeaderStyle *style, KMime::Message *message) const { xfaceSettings settings; bool useOtherPhotoSources = false; if (style->allowAsync()) { Q_ASSERT(style->nodeHelper()); Q_ASSERT(style->sourceObject()); ContactDisplayMessageMemento *photoMemento = dynamic_cast(style->nodeHelper()->bodyPartMemento( message, "contactphoto")); if (!photoMemento) { const QString email = QString::fromLatin1(KEmailAddress::firstEmailAddress(message->from()->as7BitString( false))); photoMemento = new ContactDisplayMessageMemento(email); style->nodeHelper()->setBodyPartMemento(message, "contactphoto", photoMemento); QObject::connect(photoMemento, SIGNAL(update(MimeTreeParser::UpdateMode)), style->sourceObject(), SLOT(update(MimeTreeParser::UpdateMode))); QObject::connect(photoMemento, SIGNAL(changeDisplayMail(Viewer::DisplayFormatMessage,bool)), style->sourceObject(), SIGNAL(changeDisplayMail(Viewer::DisplayFormatMessage,bool))); } if (photoMemento->finished()) { useOtherPhotoSources = true; if (photoMemento->photo().isIntern()) { // get photo data and convert to data: url const QImage photo = photoMemento->photo().data(); updateXFaceSettings(photo, settings); } else if (!photoMemento->imageFromUrl().isNull()) { updateXFaceSettings(photoMemento->imageFromUrl(), settings); } else if (!photoMemento->photo().url().isEmpty()) { settings.photoURL = photoMemento->photo().url(); if (settings.photoURL.startsWith(QLatin1Char('/'))) { settings.photoURL.prepend(QLatin1String("file:")); } } else if (!photoMemento->gravatarPixmap().isNull()) { const QImage photo = photoMemento->gravatarPixmap().toImage(); updateXFaceSettings(photo, settings); } } else { // if the memento is not finished yet, use other photo sources instead useOtherPhotoSources = true; } } else { useOtherPhotoSources = true; } if (settings.photoURL.isEmpty() && useOtherPhotoSources) { if (auto hrd = message->headerByType("Face")) { // no photo, look for a Face header const QString faceheader = hrd->asUnicodeString(); if (!faceheader.isEmpty()) { qCDebug(MESSAGEVIEWER_LOG) << "Found Face: header"; const QByteArray facestring = faceheader.toUtf8(); // Spec says header should be less than 998 bytes // Face: is 5 characters if (facestring.length() < 993) { const QByteArray facearray = QByteArray::fromBase64(facestring); QImage faceimage; if (faceimage.loadFromData(facearray, "png")) { // Spec says image must be 48x48 pixels if ((48 == faceimage.width()) && (48 == faceimage.height())) { settings.photoURL = MessageViewer::HeaderStyleUtil::imgToDataUrl( faceimage); settings.photoWidth = 48; settings.photoHeight = 48; } else { qCDebug(MESSAGEVIEWER_LOG) << "Face: header image is" << faceimage.width() << "by" << faceimage.height() << "not 48x48 Pixels"; } } else { qCDebug(MESSAGEVIEWER_LOG) << "Failed to load decoded png from Face: header"; } } else { qCDebug(MESSAGEVIEWER_LOG) << "Face: header too long at" << facestring.length(); } } } } if (settings.photoURL.isEmpty() && useOtherPhotoSources) { if (auto hrd = message->headerByType("X-Face")) { // no photo, look for a X-Face header const QString xfhead = hrd->asUnicodeString(); if (!xfhead.isEmpty()) { MessageViewer::KXFace xf; settings.photoURL = MessageViewer::HeaderStyleUtil::imgToDataUrl(xf.toImage(xfhead)); settings.photoWidth = 48; settings.photoHeight = 48; } } } return settings; } diff --git a/messageviewer/src/header/headerstyle_util.h b/messageviewer/src/header/headerstyle_util.h index 6e276eb9..e2b8a566 100644 --- a/messageviewer/src/header/headerstyle_util.h +++ b/messageviewer/src/header/headerstyle_util.h @@ -1,89 +1,90 @@ /* Copyright (C) 2013-2018 Laurent Montel This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef HEADERSTYLE_UTIL_H #define HEADERSTYLE_UTIL_H #include #include #include #include #include #include "messageviewer/spamheaderanalyzer.h" #include "messageviewer/headerstyle.h" #include "messageviewer/viewer.h" #include "messageviewer_export.h" namespace MessageViewer { class MESSAGEVIEWER_EXPORT HeaderStyleUtil { public: HeaderStyleUtil(); struct xfaceSettings { xfaceSettings() : photoWidth(60) , photoHeight(60) { } QString photoURL; int photoWidth; int photoHeight; }; enum HeaderStyleUtilDateFormat { ShortDate, /**< Locale Short date format, e.g. 08-04-2007 */ LongDate, /**< Locale Long date format, e.g. Sunday 08 April 2007 */ FancyShortDate, /**< Same as ShortDate for dates a week or more ago. For more recent dates, it is represented as Today, Yesterday, or the weekday name. */ FancyLongDate, /**< Same as LongDate for dates a week or more ago. For more recent dates, it is represented as Today, Yesterday, or the weekday name. */ CustomDate }; Q_REQUIRED_RESULT QString directionOf(const QString &str) const; - Q_REQUIRED_RESULT QString strToHtml(const QString &str, KTextToHTML::Options flags = KTextToHTML::PreserveSpaces) const; + static Q_REQUIRED_RESULT QString strToHtml(const QString &str, KTextToHTML::Options flags = KTextToHTML::PreserveSpaces); + static Q_REQUIRED_RESULT QString dateString(KMime::Message *message, HeaderStyleUtilDateFormat dateFormat); + static Q_REQUIRED_RESULT QString dateString(const KMime::Headers::Date *date, HeaderStyleUtilDateFormat dateFormat); - Q_REQUIRED_RESULT QString dateString(KMime::Message *message, bool printing, HeaderStyleUtilDateFormat dateFormat) const; Q_REQUIRED_RESULT QString subjectString(KMime::Message *message, KTextToHTML::Options flags = KTextToHTML::PreserveSpaces) const; Q_REQUIRED_RESULT QString subjectDirectionString(KMime::Message *message) const; Q_REQUIRED_RESULT QString spamStatus(KMime::Message *message) const; - Q_REQUIRED_RESULT QString dateStr(const QDateTime &dateTime) const; + static Q_REQUIRED_RESULT QString dateStr(const QDateTime &dateTime); - Q_REQUIRED_RESULT QString dateShortStr(const QDateTime &dateTime) const; + static Q_REQUIRED_RESULT QString dateShortStr(const QDateTime &dateTime); + static Q_REQUIRED_RESULT QSharedPointer resentFromList(KMime::Message *message); - Q_REQUIRED_RESULT QVector resentFromList(KMime::Message *message) const; - Q_REQUIRED_RESULT QVector resentToList(KMime::Message *message) const; + static Q_REQUIRED_RESULT QSharedPointer resentToList(KMime::Message *message); Q_REQUIRED_RESULT xfaceSettings xface(const HeaderStyle *style, KMime::Message *message) const; private: void updateXFaceSettings(QImage photo, xfaceSettings &settings) const; QString drawSpamMeter(SpamError spamError, double percent, double confidence, const QString &filterHeader, const QString &confidenceHeader) const; QString imgToDataUrl(const QImage &image) const; }; } #endif // HEADERSTYLE_UTIL_H diff --git a/messageviewer/src/header/plainheaderstyle.cpp b/messageviewer/src/header/plainheaderstyle.cpp index 8e7d47a0..3b89bb94 100644 --- a/messageviewer/src/header/plainheaderstyle.cpp +++ b/messageviewer/src/header/plainheaderstyle.cpp @@ -1,194 +1,190 @@ /* Copyright (C) 2013-2018 Laurent Montel This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "plainheaderstyle.h" #include "messageviewer/messageviewersettings.h" #include "header/headerstyle_util.h" #include "header/headerstrategy.h" #include #include #include #include using namespace MessageCore; using namespace MessageViewer; class MessageViewer::PlainHeaderStylePrivate { public: PlainHeaderStylePrivate() { } QString formatAllMessageHeaders(KMime::Message *message) const; MessageViewer::HeaderStyleUtil mHeaderStyleUtil; }; QString PlainHeaderStylePrivate::formatAllMessageHeaders(KMime::Message *message) const { QByteArray head = message->head(); KMime::Headers::Base *header = KMime::HeaderParsing::extractFirstHeader(head); QString result; while (header) { result += mHeaderStyleUtil.strToHtml(QLatin1String(header->type()) + QLatin1String( ": ") + header->asUnicodeString()); result += QLatin1String("
\n"); delete header; header = KMime::HeaderParsing::extractFirstHeader(head); } return result; } PlainHeaderStyle::PlainHeaderStyle() : HeaderStyle() , d(new MessageViewer::PlainHeaderStylePrivate) { } PlainHeaderStyle::~PlainHeaderStyle() { delete d; } // // PlainHeaderStyle: // show every header field on a line by itself, // show subject larger // QString PlainHeaderStyle::format(KMime::Message *message) const { if (!message) { return QString(); } const HeaderStrategy *strategy = headerStrategy(); // The direction of the header is determined according to the direction // of the application layout. const QString dir = QApplication::isRightToLeft() ? QStringLiteral("rtl") : QStringLiteral("ltr"); // However, the direction of the message subject within the header is // determined according to the contents of the subject itself. Since // the "Re:" and "Fwd:" prefixes would always cause the subject to be // considered left-to-right, they are ignored when determining its // direction. const QString subjectDir = d->mHeaderStyleUtil.subjectDirectionString(message); QString headerStr; if (strategy->headersToDisplay().isEmpty() && strategy->defaultPolicy() == HeaderStrategy::Display) { // crude way to emulate "all" headers - Note: no strings have // i18n(), so direction should always be ltr. headerStr = QStringLiteral("
"); headerStr += d->formatAllMessageHeaders(message); return headerStr + QLatin1String("
"); } headerStr = QStringLiteral("
").arg(dir); //case HdrLong: if (strategy->showHeader(QStringLiteral("subject"))) { KTextToHTML::Options flags = KTextToHTML::PreserveSpaces; if (showEmoticons()) { flags |= KTextToHTML::ReplaceSmileys; } headerStr += QStringLiteral("
").arg(subjectDir) +d->mHeaderStyleUtil.subjectString(message, flags) + QLatin1String( "
\n"); } if (strategy->showHeader(QStringLiteral("date"))) { + const auto dateFormat = isPrinting()? MessageViewer::HeaderStyleUtil::ShortDate : MessageViewer::HeaderStyleUtil::CustomDate; headerStr.append(i18n("Date: ") - + d->mHeaderStyleUtil.strToHtml(d->mHeaderStyleUtil.dateString(message, - isPrinting(), - /* short = */ - MessageViewer - :: - HeaderStyleUtil - ::CustomDate)) + + HeaderStyleUtil::strToHtml(HeaderStyleUtil::dateString(message, + dateFormat)) + QLatin1String("
\n")); } if (strategy->showHeader(QStringLiteral("from"))) { headerStr.append(i18n("From: ") +StringUtil::emailAddrAsAnchor(message->from(), StringUtil::DisplayFullAddress, QString(), StringUtil::ShowLink)); if (!vCardName().isEmpty()) { headerStr.append(QLatin1String("  ") + i18n("[vCard]") + QLatin1String("")); } if (strategy->showHeader(QStringLiteral("organization")) && message->organization(false)) { headerStr.append(QLatin1String("  (") +d->mHeaderStyleUtil.strToHtml( message->organization()->asUnicodeString()) + QLatin1Char(')')); } headerStr.append(QLatin1String("
\n")); } if (strategy->showHeader(QStringLiteral("to"))) { headerStr.append(i18nc("To-field of the mailheader.", "To: ") +StringUtil::emailAddrAsAnchor( message->to(), StringUtil::DisplayFullAddress) + QLatin1String("
\n")); } if (strategy->showHeader(QStringLiteral("cc")) && message->cc(false)) { const QString str = StringUtil::emailAddrAsAnchor( message->cc(), StringUtil::DisplayFullAddress); if (!str.isEmpty()) { headerStr.append(i18n("CC: ") + str + QLatin1String("
\n")); } } if (strategy->showHeader(QStringLiteral("bcc")) && message->bcc(false)) { const QString str = StringUtil::emailAddrAsAnchor( message->bcc(), StringUtil::DisplayFullAddress); if (!str.isEmpty()) { headerStr.append(i18n("BCC: ") + str + QLatin1String("
\n")); } } if (strategy->showHeader(QStringLiteral("reply-to")) && message->replyTo(false)) { headerStr.append(i18n("Reply to: ") +StringUtil::emailAddrAsAnchor( message->replyTo(), StringUtil::DisplayFullAddress) + QLatin1String("
\n")); } headerStr += QLatin1String("
\n"); return headerStr; } const char *MessageViewer::PlainHeaderStyle::name() const { return "plain"; } diff --git a/messageviewer/src/messageviewerheaderplugins/defaultgrantleeheaderstyleplugin/theme/5.2/header.html b/messageviewer/src/messageviewerheaderplugins/defaultgrantleeheaderstyleplugin/theme/5.2/header.html index 83d99826..c4f0806f 100644 --- a/messageviewer/src/messageviewerheaderplugins/defaultgrantleeheaderstyleplugin/theme/5.2/header.html +++ b/messageviewer/src/messageviewerheaderplugins/defaultgrantleeheaderstyleplugin/theme/5.2/header.html @@ -1,87 +1,87 @@
{{ header.subject|safe }}
{% if header.photourl %} {% else %} {% endif %}
{{ header.fromi18n }}
- {{ header.from|safe }} - {% if header.resentfrom %} - {{ header.resentfromi18n }}: {{ header.resentfrom|safe }} + {{ header.from.fullAddress|safe }} + {% if header.resentfrom.isSet %} + {{ header.resentfromi18n }}: {{ header.resentfrom.fullAddress|safe }} {% endif %} {% if header.vcardname %} {{ header.vcardi18n }} {% endif %} {% if header.organization %} ({{ header.organization|safe }}) {% endif %}
- {% if header.to %} + {% if header.to.isSet %}
{{ header.toi18n }}
-
{{ header.toExpandable|safe }}
+
{{ header.to.expandableTo|safe }}
{% endif %} - {% if header.cc %} + {% if header.cc.isSet %}
{{ header.cci18n }}
-
{{ header.ccExpandable|safe }}
+
{{ header.cc.expandableCc|safe }}
{% endif %} - {% if header.bcc %} + {% if header.bcc.isSet %}
{{ header.bcci18n }}
-
{{ header.bcc|safe }}
+
{{ header.bcc.fullAddress|safe }}
{% endif %} {% if header.sender %}
{{ header.senderi18n }}
{{ header.sender|safe }}
{% endif %} {% if header.listid %}
{{ header.listidi18n }}
{{ header.listid }}
{% endif %}
{{ header.datei18n }}
-
{{ header.dateshort }}
+
{{ header.date.short }}
{% if header.spamHTML %}
{{ header.spamstatusi18n }}
{{ header.spamHTML|safe }}
{% endif %} {% if header.messageHasSecurityInfo %}
{{ header.messageHasSecurityInfoI18n }}
{% if header.messageIsSigned %} {{ header.signedIcon|safe }} {% endif %} {% if header.messageIsEncrypted %} {{ header.encryptedIcon|safe }} {% endif %}
{% endif %}
{% if header.hasAttachment %}
{{ header.attachmentHtml|safe }}
{% endif %}