7#include "grantleeheaderformatter.h"
8#include "headerstyle_util.h"
9#include "settings/messageviewersettings.h"
10#include "utils/iconnamecache.h"
12#include <MessageCore/StringUtil>
13#include <MimeTreeParser/NodeHelper>
15#include <KMime/Message>
17#include <KColorScheme>
19#include <KLocalizedString>
20#include <KTextTemplate/Engine>
21#include <KTextTemplate/MetaType>
23using namespace MessageCore;
25using namespace MessageViewer;
37inline QVariant TypeAccessor<const KMime::Headers::Generics::AddressList *>::lookUp(
const KMime::Headers::Generics::AddressList *
const object,
38 const QString &property)
40 if (property == QLatin1StringView(
"nameOnly")) {
41 return StringUtil::emailAddrAsAnchor(
object, StringUtil::DisplayNameOnly);
42 }
else if (property == QLatin1StringView(
"isSet")) {
43 return !
object->asUnicodeString().isEmpty();
44 }
else if (property == QLatin1StringView(
"fullAddress")) {
45 return StringUtil::emailAddrAsAnchor(
object, StringUtil::DisplayFullAddress);
46 }
else if (property == QLatin1StringView(
"str")) {
47 return object->asUnicodeString();
48 }
else if (property.
startsWith(QLatin1StringView(
"expandable"))) {
49 const auto &
name =
property.
mid(10);
50 const QString val = MessageCore::StringUtil::emailAddrAsAnchor(
object,
51 MessageCore::StringUtil::DisplayFullAddress,
53 MessageCore::StringUtil::ShowLink,
54 MessageCore::StringUtil::ExpandableAddresses,
55 QStringLiteral(
"Full") + name + QStringLiteral(
"AddressList"));
62inline QVariant TypeAccessor<QByteArray &>::lookUp(
const QByteArray &
object,
const QString &property)
69 return StringUtil::emailAddrAsAnchor(
object.data(), StringUtil::DisplayNameOnly);
71 return !
object->asUnicodeString().isEmpty();
73 return StringUtil::emailAddrAsAnchor(
object.data(), StringUtil::DisplayFullAddress);
75 return object->asUnicodeString();
77 const auto &
name =
property.
mid(10);
78 const QString val = MessageCore::StringUtil::emailAddrAsAnchor(
object.data(),
79 MessageCore::StringUtil::DisplayFullAddress,
81 MessageCore::StringUtil::ShowLink,
82 MessageCore::StringUtil::ExpandableAddresses,
83 QStringLiteral(
"Full") + name + QStringLiteral(
"AddressList"));
92inline QVariant TypeAccessor<const KMime::Headers::Generics::MailboxList *>::lookUp(
const KMime::Headers::Generics::MailboxList *
const object,
93 const QString &property)
95 if (property == QLatin1StringView(
"nameOnly")) {
96 return StringUtil::emailAddrAsAnchor(
object, StringUtil::DisplayNameOnly);
97 }
else if (property == QLatin1StringView(
"isSet")) {
98 return !
object->asUnicodeString().isEmpty();
99 }
else if (property == QLatin1StringView(
"fullAddress")) {
100 return StringUtil::emailAddrAsAnchor(
object, StringUtil::DisplayFullAddress);
101 }
else if (property == QLatin1StringView(
"str")) {
102 return object->asUnicodeString();
103 }
else if (property.
startsWith(QLatin1StringView(
"expandable"))) {
104 const auto &
name =
property.
mid(10);
105 const QString val = MessageCore::StringUtil::emailAddrAsAnchor(
object,
106 MessageCore::StringUtil::DisplayFullAddress,
108 MessageCore::StringUtil::ShowLink,
109 MessageCore::StringUtil::ExpandableAddresses,
110 QStringLiteral(
"Full") + name + QStringLiteral(
"AddressList"));
118 return StringUtil::emailAddrAsAnchor(
object.data(), StringUtil::DisplayNameOnly);
120 return !
object->asUnicodeString().isEmpty();
122 return StringUtil::emailAddrAsAnchor(
object.data(), StringUtil::DisplayFullAddress);
124 return object->asUnicodeString();
126 const auto &
name =
property.
mid(10);
127 const QString val = MessageCore::StringUtil::emailAddrAsAnchor(
object.data(),
128 MessageCore::StringUtil::DisplayFullAddress,
130 MessageCore::StringUtil::ShowLink,
131 MessageCore::StringUtil::ExpandableAddresses,
132 QStringLiteral(
"Full") + name + QStringLiteral(
"AddressList"));
139inline QVariant TypeAccessor<QDateTime &>::lookUp(
const QDateTime &
object,
const QString &property)
142 if (property == QLatin1StringView(
"str")) {
143 return HeaderStyleUtil::dateStr(
object);
144 }
else if (property == QLatin1StringView(
"short")) {
146 }
else if (property == QLatin1StringView(
"long")) {
148 }
else if (property == QLatin1StringView(
"fancylong")) {
150 }
else if (property == QLatin1StringView(
"fancyshort")) {
152 }
else if (property == QLatin1StringView(
"localelong")) {
158 return HeaderStyleUtil::strToHtml(HeaderStyleUtil::dateString(
object, dateFormat));
162class Q_DECL_HIDDEN HeaderFormatter
165 virtual ~HeaderFormatter() =
default;
167 virtual QVariant format(KMime::Message *message, MimeTreeParser::NodeHelper *nodeHelper,
bool showEmoticons) = 0;
168 virtual QString i18nName() = 0;
171class DefaultHeaderFormatter :
public HeaderFormatter
174 DefaultHeaderFormatter(
const QByteArray &h)
179 QString i18nName()
override
181 if (header ==
"list-id") {
182 return i18n(
"List-Id:");
188 QVariant format(KMime::Message *message, MimeTreeParser::NodeHelper *nodeHelper,
bool showEmoticons)
override
190 Q_UNUSED(showEmoticons);
191 return nodeHelper->mailHeaderAsBase(header.constData(), message)->
asUnicodeString();
198class SubjectFormatter :
public HeaderFormatter
201 QString i18nName()
override
203 return i18n(
"Subject:");
206 QVariant format(KMime::Message *message, MimeTreeParser::NodeHelper *nodeHelper,
bool showEmoticons)
override
212 const auto subjectStr = nodeHelper->mailHeaderAsBase(
"subject", message)->
asUnicodeString();
214 return HeaderStyleUtil::strToHtml(subjectStr, flags);
218class GrantleeHeaderDateFormatter :
public HeaderFormatter
221 QString i18nName()
override
223 return i18n(
"Date:");
226 QVariant format(KMime::Message *message, MimeTreeParser::NodeHelper *nodeHelper,
bool showEmoticons)
override
228 Q_UNUSED(showEmoticons);
229 const auto value = nodeHelper->dateHeader(message);
234class MessageIdFormatter :
public HeaderFormatter
237 QString i18nName()
override
239 return i18n(
"Message-Id:");
242 QVariant format(KMime::Message *message, MimeTreeParser::NodeHelper *nodeHelper,
bool showEmoticons)
override
244 const auto messageIdHeader = nodeHelper->mailHeaderAsBase(
"Message-Id", message);
245 if (messageIdHeader !=
nullptr) {
246 return static_cast<const KMime::Headers::MessageID *
>(messageIdHeader)->identifier();
252class AddressHeaderFormatter :
public HeaderFormatter
255 AddressHeaderFormatter(
const QByteArray &h)
260 QString i18nName()
override
262 if (header ==
"to") {
264 }
else if (header ==
"reply-To") {
265 return i18n(
"Reply To:");
266 }
else if (header ==
"cc") {
268 }
else if (header ==
"bcc") {
270 }
else if (header ==
"from") {
271 return i18n(
"From:");
272 }
else if (header ==
"sender") {
273 return i18n(
"Sender:");
274 }
else if (header ==
"resent-From") {
275 return i18n(
"resent from:");
276 }
else if (header ==
"resent-To") {
277 return i18n(
"resent to:");
283 QVariant format(KMime::Message *message, MimeTreeParser::NodeHelper *nodeHelper,
bool showEmoticons)
override
285 Q_UNUSED(showEmoticons);
286 const auto value = nodeHelper->mailHeaderAsAddressList(header.constData(), message);
294class MessageViewer::GrantleeHeaderFormatter::GrantleeHeaderFormatterPrivate
297 GrantleeHeaderFormatterPrivate()
298 : engine(new KTextTemplate::Engine)
307 templateLoader = QSharedPointer<KTextTemplate::FileSystemTemplateLoader>(
new KTextTemplate::FileSystemTemplateLoader);
308 engine->addTemplateLoader(templateLoader);
310 QList<QByteArray> addressHeaders;
311 addressHeaders <<
"to"
320 for (
const auto &header : std::as_const(addressHeaders)) {
321 registerHeaderFormatter(header, QSharedPointer<HeaderFormatter>(
new AddressHeaderFormatter(header)));
324 registerHeaderFormatter(
"subject", QSharedPointer<HeaderFormatter>(
new SubjectFormatter()));
325 registerHeaderFormatter(
"date", QSharedPointer<HeaderFormatter>(
new GrantleeHeaderDateFormatter()));
326 registerHeaderFormatter(
"Message-Id", QSharedPointer<HeaderFormatter>(
new MessageIdFormatter()));
329 ~GrantleeHeaderFormatterPrivate()
334 void registerHeaderFormatter(
const QByteArray &header, QSharedPointer<HeaderFormatter> formatter)
336 headerFormatter[header] = formatter;
338 QSharedPointer<KTextTemplate::FileSystemTemplateLoader> templateLoader;
339 KTextTemplate::Engine *
const engine;
340 QMap<QByteArray, QSharedPointer<HeaderFormatter>> headerFormatter;
341 MessageViewer::HeaderStyleUtil headerStyleUtil;
347GrantleeHeaderFormatter::GrantleeHeaderFormatter()
352GrantleeHeaderFormatter::~GrantleeHeaderFormatter() =
default;
354QString GrantleeHeaderFormatter::toHtml(
const GrantleeHeaderFormatter::GrantleeHeaderFormatterSettings &settings)
const
357 if (!settings.theme.isValid()) {
358 errorMessage =
i18n(
"Grantlee theme \"%1\" is not valid.", settings.theme.name());
361 d->templateLoader->setTemplateDirs(QStringList() << settings.theme.absolutePath());
362 KTextTemplate::Template headerTemplate = d->engine->loadByName(settings.theme.themeFilename());
363 if (headerTemplate->
error()) {
367 return format(settings.theme.absolutePath(),
369 settings.theme.displayExtraVariables(),
373 settings.showEmoticons);
376QString GrantleeHeaderFormatter::toHtml(
const QStringList &displayExtraHeaders,
377 const QString &absolutPath,
378 const QString &filename,
379 const MessageViewer::HeaderStyle *style,
380 KMime::Message *message,
381 bool isPrinting)
const
383 d->templateLoader->setTemplateDirs(QStringList() << absolutPath);
385 if (headerTemplate->
error()) {
388 return format(absolutPath, headerTemplate, displayExtraHeaders, isPrinting, style, message);
391QString GrantleeHeaderFormatter::format(
const QString &absolutePath,
392 const KTextTemplate::Template &headerTemplate,
393 const QStringList &displayExtraHeaders,
395 const MessageViewer::HeaderStyle *style,
396 KMime::Message *message,
397 bool showEmoticons)
const
399 QVariantHash headerObject;
400 const auto nodeHelper = style->nodeHelper();
408 headerObject.
insert(QStringLiteral(
"absoluteThemePath"), absoluteThemePath);
412 headerObject.
insert(QStringLiteral(
"subjectDir"), d->headerStyleUtil.subjectDirectionString(message));
414 QList<QByteArray> defaultHeaders;
415 defaultHeaders <<
"to"
429 for (
const auto &header : std::as_const(defaultHeaders)) {
430 QSharedPointer<HeaderFormatter> formatter;
431 if (d->headerFormatter.contains(header)) {
432 formatter = d->headerFormatter.value(header);
434 formatter = QSharedPointer<HeaderFormatter>(
new DefaultHeaderFormatter(header));
436 const auto i18nName = formatter->i18nName();
438 if (nodeHelper->hasMailHeader(header.constData(), message)) {
439 const auto value = formatter->format(message, nodeHelper, showEmoticons);
440 headerObject.insert(objectName, value);
442 if (!i18nName.isEmpty()) {
443 headerObject.insert(objectName + QStringLiteral(
"i18n"), i18nName);
447 if (!nodeHelper->hasMailHeader(
"subject", message)) {
448 headerObject.insert(QStringLiteral(
"subject"),
i18n(
"No Subject"));
451 const QString spamHtml = d->headerStyleUtil.spamStatus(message);
453 headerObject.insert(QStringLiteral(
"spamstatusi18n"),
i18n(
"Spam Status:"));
454 headerObject.insert(QStringLiteral(
"spamHTML"), spamHtml);
457 if (!style->vCardName().
isEmpty()) {
458 headerObject.insert(QStringLiteral(
"vcardname"), style->vCardName());
465 headerObject.insert(QStringLiteral(
"isprinting"),
i18n(
"Printing mode"));
466 headerObject.insert(QStringLiteral(
"printmode"), QStringLiteral(
"printmode"));
468 headerObject.insert(QStringLiteral(
"screenmode"), QStringLiteral(
"screenmode"));
473 QString linkColor = QStringLiteral(
"white");
475 if (!d->activeColor.isValid()) {
478 QColor activeColorDark = d->activeColor.darker(130);
480 if (!style->isTopLevel()) {
481 activeColorDark = d->activeColor.
darker(50);
483 linkColor = QStringLiteral(
"black");
487 headerObject.insert(QStringLiteral(
"activecolordark"), activeColorDark.
name());
488 headerObject.insert(QStringLiteral(
"fontcolor"), fontColor.name());
489 headerObject.insert(QStringLiteral(
"linkcolor"), linkColor);
491 MessageViewer::HeaderStyleUtil::xfaceSettings xface = d->headerStyleUtil.xface(style, message);
492 if (!xface.photoURL.
isEmpty()) {
493 headerObject.insert(QStringLiteral(
"photowidth"), xface.photoWidth);
494 headerObject.insert(QStringLiteral(
"photoheight"), xface.photoHeight);
495 headerObject.insert(QStringLiteral(
"photourl"), xface.photoURL);
498 for (QString header : std::as_const(displayExtraHeaders)) {
499 const QByteArray baHeader = header.toLocal8Bit();
500 if (
auto hrd = message->headerByType(baHeader.
constData())) {
502 header.remove(QLatin1Char(
'-'));
503 headerObject.insert(header, hrd->asUnicodeString());
507 headerObject.insert(QStringLiteral(
"vcardi18n"),
i18n(
"[vcard]"));
508 headerObject.insert(QStringLiteral(
"readOnlyMessage"), style->readOnlyMessage());
510 const QString attachmentHtml = style->attachmentHtml();
511 const bool messageHasAttachment = !attachmentHtml.
isEmpty();
512 headerObject.insert(QStringLiteral(
"hasAttachment"), messageHasAttachment);
513 headerObject.insert(QStringLiteral(
"attachmentHtml"), attachmentHtml);
514 headerObject.insert(QStringLiteral(
"attachmentI18n"),
i18n(
"Attachments:"));
516 if (messageHasAttachment) {
517 const QString iconPath = MessageViewer::IconNameCache::instance()->iconPath(QStringLiteral(
"mail-attachment"),
KIconLoader::Toolbar);
518 const QString html = QStringLiteral(
"<img height=\"%2\" width=\"%2\" src=\"%1\"></a>").
arg(iconPath,
QString::number(d->iconSize));
519 headerObject.
insert(QStringLiteral(
"attachmentIcon"), html);
522 const bool messageIsSigned = KMime::isSigned(message);
523 headerObject.insert(QStringLiteral(
"messageIsSigned"), messageIsSigned);
524 if (messageIsSigned) {
525 const QString iconPath = MessageViewer::IconNameCache::instance()->iconPath(QStringLiteral(
"mail-signed"),
KIconLoader::Toolbar);
526 const QString html = QStringLiteral(
"<img height=\"%2\" width=\"%2\" src=\"%1\"></a>").
arg(iconPath,
QString::number(d->iconSize));
527 headerObject.
insert(QStringLiteral(
"signedIcon"), html);
530 const bool messageIsEncrypted = KMime::isEncrypted(message);
531 headerObject.insert(QStringLiteral(
"messageIsEncrypted"), messageIsEncrypted);
532 if (messageIsEncrypted) {
533 const QString iconPath = MessageViewer::IconNameCache::instance()->iconPath(QStringLiteral(
"mail-encrypted"),
KIconLoader::Toolbar);
534 const QString html = QStringLiteral(
"<img height=\"%2\" width=\"%2\" src=\"%1\"></a>").
arg(iconPath,
QString::number(d->iconSize));
535 headerObject.
insert(QStringLiteral(
"encryptedIcon"), html);
538 const bool messageHasSecurityInfo = messageIsEncrypted || messageIsSigned;
539 headerObject.insert(QStringLiteral(
"messageHasSecurityInfo"), messageHasSecurityInfo);
540 headerObject.insert(QStringLiteral(
"messageHasSecurityInfoI18n"),
i18n(
"Security:"));
542 QVariantHash mapping;
543 mapping.insert(QStringLiteral(
"header"), headerObject);
544 KTextTemplate::Context context(mapping);
546 return headerTemplate->
render(&context);
int currentSize(KIconLoader::Group group) const
static KIconLoader * global()
Template loadByName(const QString &name) const
QString errorString() const
QString render(Context *c) const
Engine const * engine() const
QString i18n(const char *text, const TYPE &arg...)
KCALUTILS_EXPORT QString errorMessage(const KCalendarCore::Exception &exception)
QString name(StandardAction id)
const char * constData() const const
QColor darker(int factor) const const
QString name(NameFormat format) const const
QString arg(Args &&... args) const const
QString fromUtf8(QByteArrayView str)
QString & insert(qsizetype position, QChar ch)
bool isEmpty() const const
QString mid(qsizetype position, qsizetype n) const const
QString number(double n, char format, int precision)
QString & remove(QChar ch, Qt::CaseSensitivity cs)
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
QUrl fromLocalFile(const QString &localFile)
QString url(FormattingOptions options) const const
QVariant fromValue(T &&value)