1 /*
2 This file is part of KAddressBook.
3
4 SPDX-FileCopyrightText: 2010 Tobias Koenig <tokoe@kde.org>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8
9 #include "grantleecontactformatter.h"
10 #include "contactgrantleewrapper.h"
11
12 #include <KContacts/Addressee>
13
14 #include <GrantleeTheme/GrantleeKi18nLocalizer>
15 #include <GrantleeTheme/GrantleeTheme>
16 #include <GrantleeTheme/GrantleeThemeEngine>
17
18 #include <grantlee/context.h>
19 #include <grantlee/metatype.h>
20 #include <grantlee/templateloader.h>
21
22 #include <Akonadi/Item>
23
24 #include <KColorScheme>
25
26 #include <KConfigGroup>
27 #include <KLocalizedString>
28 #include <KStringHandler>
29
30 #include <QLocale>
31 #include <QMetaProperty>
32 #include <QSet>
33
34 using namespace KAddressBookGrantlee;
35
36 GRANTLEE_BEGIN_LOOKUP(QUrl)
37 if (property == QLatin1String("scheme")) {
38 return object.scheme();
39 } else if (property == QLatin1String("path")) {
40 return object.path();
41 }
42 GRANTLEE_END_LOOKUP
43
44 class KAddressBookGrantlee::GrantleeContactFormatterPrivate
45 {
46 public:
GrantleeContactFormatterPrivate()47 GrantleeContactFormatterPrivate()
48 {
49 KConfig config(QStringLiteral("akonadi_contactrc"));
50 KConfigGroup group(&config, QStringLiteral("View"));
51 showQRCode = group.readEntry("QRCodes", true);
52
53 mEngine = std::make_unique<GrantleeTheme::Engine>();
54
55 mTemplateLoader = QSharedPointer<Grantlee::FileSystemTemplateLoader>(new Grantlee::FileSystemTemplateLoader());
56 }
57
~GrantleeContactFormatterPrivate()58 ~GrantleeContactFormatterPrivate()
59 {
60 mTemplateLoader.clear();
61 }
62
changeGrantleePath(const QString & path)63 void changeGrantleePath(const QString &path)
64 {
65 mTemplateLoader->setTemplateDirs(QStringList() << path);
66 mEngine->addTemplateLoader(mTemplateLoader);
67
68 mSelfcontainedTemplate = mEngine->loadByName(QStringLiteral("contact.html"));
69 if (mSelfcontainedTemplate->error()) {
70 mErrorMessage += mSelfcontainedTemplate->errorString() + QStringLiteral("<br>");
71 }
72
73 mEmbeddableTemplate = mEngine->loadByName(QStringLiteral("contact_embedded.html"));
74 if (mEmbeddableTemplate->error()) {
75 mErrorMessage += mEmbeddableTemplate->errorString() + QStringLiteral("<br>");
76 }
77 }
78
79 QVector<QObject *> mObjects;
80 std::unique_ptr<GrantleeTheme::Engine> mEngine;
81 QSharedPointer<Grantlee::FileSystemTemplateLoader> mTemplateLoader;
82 Grantlee::Template mSelfcontainedTemplate;
83 Grantlee::Template mEmbeddableTemplate;
84 QString mErrorMessage;
85 bool forceDisableQRCode = false;
86 bool showQRCode = true;
87 };
88
GrantleeContactFormatter()89 GrantleeContactFormatter::GrantleeContactFormatter()
90 : d(new GrantleeContactFormatterPrivate)
91 {
92 Grantlee::registerMetaType<QUrl>();
93 }
94
95 GrantleeContactFormatter::~GrantleeContactFormatter() = default;
96
setAbsoluteThemePath(const QString & path)97 void GrantleeContactFormatter::setAbsoluteThemePath(const QString &path)
98 {
99 d->changeGrantleePath(path);
100 }
101
setGrantleeTheme(const GrantleeTheme::Theme & theme)102 void GrantleeContactFormatter::setGrantleeTheme(const GrantleeTheme::Theme &theme)
103 {
104 d->changeGrantleePath(theme.absolutePath());
105 }
106
setForceDisableQRCode(bool b)107 void GrantleeContactFormatter::setForceDisableQRCode(bool b)
108 {
109 d->forceDisableQRCode = b;
110 }
111
forceDisableQRCode() const112 bool GrantleeContactFormatter::forceDisableQRCode() const
113 {
114 return d->forceDisableQRCode;
115 }
116
setShowQRCode(bool b)117 void GrantleeContactFormatter::setShowQRCode(bool b)
118 {
119 d->showQRCode = b;
120 }
121
toHtml(HtmlForm form) const122 QString GrantleeContactFormatter::toHtml(HtmlForm form) const
123 {
124 if (!d->mErrorMessage.isEmpty()) {
125 return d->mErrorMessage;
126 }
127
128 KContacts::Addressee rawContact;
129 const Akonadi::Item localItem = item();
130 if (localItem.isValid() && localItem.hasPayload<KContacts::Addressee>()) {
131 rawContact = localItem.payload<KContacts::Addressee>();
132 } else {
133 rawContact = contact();
134 }
135
136 if (rawContact.isEmpty()) {
137 return QString();
138 }
139
140 // Custom fields
141 QVariantList customFields;
142 QVariantList customFieldsUrl;
143 static QSet<QString> blacklistedKeys = {QStringLiteral("CRYPTOPROTOPREF"),
144 QStringLiteral("OPENPGPFP"),
145 QStringLiteral("SMIMEFP"),
146 QStringLiteral("CRYPTOSIGNPREF"),
147 QStringLiteral("CRYPTOENCRYPTPREF"),
148 QStringLiteral("Anniversary"),
149 QStringLiteral("BlogFeed"),
150 QStringLiteral("Profession"),
151 QStringLiteral("Office"),
152 QStringLiteral("ManagersName"),
153 QStringLiteral("AssistantsName"),
154 QStringLiteral("SpousesName"),
155 QStringLiteral("IMAddress"),
156 QStringLiteral("AddressBook"),
157 QStringLiteral("MailPreferedFormatting"),
158 QStringLiteral("MailAllowToRemoteContent"),
159 QStringLiteral("MAILPREFEREDFORMATTING"),
160 QStringLiteral("MAILALLOWTOREMOTECONTENT")};
161
162 const auto customs = rawContact.customs();
163 for (QString custom : customs) {
164 if (custom.startsWith(QLatin1String("KADDRESSBOOK-"))) {
165 custom.remove(QStringLiteral("KADDRESSBOOK-X-"));
166 custom.remove(QStringLiteral("KADDRESSBOOK-"));
167
168 int pos = custom.indexOf(QLatin1Char(':'));
169 QString key = custom.left(pos);
170 QString value = custom.mid(pos + 1);
171
172 if (blacklistedKeys.contains(key)) {
173 continue;
174 }
175
176 bool addUrl = false;
177 // check whether it is a custom local field
178 for (int i = 0; i < customFieldDescriptions().size(); ++i) {
179 const QVariantMap description = customFieldDescriptions().at(i);
180 if (description.value(QStringLiteral("key")).toString() == key) {
181 key = description.value(QStringLiteral("title")).toString();
182 const QString descriptionType = description.value(QStringLiteral("type")).toString();
183 if (descriptionType == QLatin1String("boolean")) {
184 if (value == QLatin1String("true")) {
185 value = i18nc("Boolean value", "yes");
186 } else {
187 value = i18nc("Boolean value", "no");
188 }
189 } else if (descriptionType == QLatin1String("date")) {
190 const QDate date = QDate::fromString(value, Qt::ISODate);
191 value = QLocale().toString(date, QLocale::ShortFormat);
192 } else if (descriptionType == QLatin1String("time")) {
193 const QTime time = QTime::fromString(value, Qt::ISODate);
194 value = QLocale::system().toString(time, QLocale::ShortFormat);
195 } else if (descriptionType == QLatin1String("datetime")) {
196 const QDateTime dateTime = QDateTime::fromString(value, Qt::ISODate);
197 value = QLocale().toString(dateTime, QLocale::ShortFormat);
198 } else if (descriptionType == QLatin1String("url")) {
199 value = KStringHandler::tagUrls(value.toHtmlEscaped());
200 addUrl = true;
201 }
202 break;
203 }
204 }
205 QVariantHash customFieldObject;
206 customFieldObject.insert(QStringLiteral("title"), key);
207 customFieldObject.insert(QStringLiteral("value"), value);
208
209 if (addUrl) {
210 customFieldsUrl.append(customFieldObject);
211 } else {
212 customFields.append(customFieldObject);
213 }
214 }
215 }
216
217 QVariantHash colorsObject;
218
219 colorsObject.insert(QStringLiteral("linkColor"), KColorScheme(QPalette::Active, KColorScheme::View).foreground().color().name());
220
221 colorsObject.insert(QStringLiteral("textColor"), KColorScheme(QPalette::Active, KColorScheme::View).foreground().color().name());
222
223 colorsObject.insert(QStringLiteral("backgroundColor"), KColorScheme(QPalette::Active, KColorScheme::View).background().color().name());
224
225 QVariantHash mapping;
226 mapping.insert(QStringLiteral("contact"), QVariant::fromValue(ContactGrantleeWrapper(rawContact)));
227 mapping.insert(QStringLiteral("colors"), colorsObject);
228 mapping.insert(QStringLiteral("customFields"), customFields);
229 mapping.insert(QStringLiteral("customFieldsUrl"), customFieldsUrl);
230 mapping.insert(QStringLiteral("hasqrcode"), !d->forceDisableQRCode && d->showQRCode);
231
232 Grantlee::Context context(mapping);
233 context.setLocalizer(d->mEngine->localizer());
234
235 if (form == SelfcontainedForm) {
236 return d->mSelfcontainedTemplate->render(&context);
237 } else if (form == EmbeddableForm) {
238 return d->mEmbeddableTemplate->render(&context);
239 } else {
240 return QString();
241 }
242 }
243
setApplicationDomain(const QByteArray & domain)244 void GrantleeContactFormatter::setApplicationDomain(const QByteArray &domain)
245 {
246 d->mEngine->localizer()->setApplicationDomain(domain);
247 }
248