File indexing completed on 2025-02-16 04:45:49

0001 /*
0002   This file is part of KAddressBook.
0003 
0004   SPDX-FileCopyrightText: 2010 Tobias Koenig <tokoe@kde.org>
0005 
0006   SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "grantleecontactformatter.h"
0010 #include "contactgrantleewrapper.h"
0011 
0012 #include <KContacts/Addressee>
0013 
0014 #include <GrantleeTheme/GrantleeKi18nLocalizer>
0015 #include <GrantleeTheme/GrantleeTheme>
0016 #include <GrantleeTheme/GrantleeThemeEngine>
0017 #include <KTextTemplate/Context>
0018 #include <KTextTemplate/MetaType>
0019 #include <KTextTemplate/TemplateLoader>
0020 
0021 #include <Akonadi/Item>
0022 
0023 #include <KColorScheme>
0024 
0025 #include <KConfigGroup>
0026 #include <KLocalizedString>
0027 #include <KStringHandler>
0028 
0029 #include <QLocale>
0030 #include <QMetaProperty>
0031 #include <QSet>
0032 
0033 using namespace KAddressBookGrantlee;
0034 
0035 KTEXTTEMPLATE_BEGIN_LOOKUP(QUrl)
0036 if (property == QLatin1StringView("scheme")) {
0037     return object.scheme();
0038 } else if (property == QLatin1StringView("path")) {
0039     return object.path();
0040 }
0041 KTEXTTEMPLATE_END_LOOKUP
0042 
0043 class KAddressBookGrantlee::GrantleeContactFormatterPrivate
0044 {
0045 public:
0046     GrantleeContactFormatterPrivate()
0047     {
0048         KConfig config(QStringLiteral("akonadi_contactrc"));
0049         KConfigGroup group(&config, QStringLiteral("View"));
0050         showQRCode = group.readEntry("QRCodes", true);
0051         mEngine = std::make_unique<GrantleeTheme::Engine>();
0052         mTemplateLoader = QSharedPointer<KTextTemplate::FileSystemTemplateLoader>(new KTextTemplate::FileSystemTemplateLoader());
0053     }
0054 
0055     ~GrantleeContactFormatterPrivate()
0056     {
0057         mTemplateLoader.clear();
0058     }
0059 
0060     void changeGrantleePath(const QString &path)
0061     {
0062         mTemplateLoader->setTemplateDirs(QStringList() << path);
0063         mEngine->addTemplateLoader(mTemplateLoader);
0064 
0065         mSelfcontainedTemplate = mEngine->loadByName(QStringLiteral("contact.html"));
0066         if (mSelfcontainedTemplate->error()) {
0067             mErrorMessage += mSelfcontainedTemplate->errorString() + QStringLiteral("<br>");
0068         }
0069 
0070         mEmbeddableTemplate = mEngine->loadByName(QStringLiteral("contact_embedded.html"));
0071         if (mEmbeddableTemplate->error()) {
0072             mErrorMessage += mEmbeddableTemplate->errorString() + QStringLiteral("<br>");
0073         }
0074     }
0075 
0076     QList<QObject *> mObjects;
0077     std::unique_ptr<GrantleeTheme::Engine> mEngine;
0078     QSharedPointer<KTextTemplate::FileSystemTemplateLoader> mTemplateLoader;
0079     KTextTemplate::Template mSelfcontainedTemplate;
0080     KTextTemplate::Template mEmbeddableTemplate;
0081     QString mErrorMessage;
0082     bool forceDisableQRCode = false;
0083     bool showQRCode = true;
0084 };
0085 
0086 GrantleeContactFormatter::GrantleeContactFormatter()
0087     : d(new GrantleeContactFormatterPrivate)
0088 {
0089     KTextTemplate::registerMetaType<QUrl>();
0090 }
0091 
0092 GrantleeContactFormatter::~GrantleeContactFormatter() = default;
0093 
0094 void GrantleeContactFormatter::setAbsoluteThemePath(const QString &path)
0095 {
0096     d->changeGrantleePath(path);
0097 }
0098 
0099 void GrantleeContactFormatter::setGrantleeTheme(const GrantleeTheme::Theme &theme)
0100 {
0101     d->changeGrantleePath(theme.absolutePath());
0102 }
0103 
0104 void GrantleeContactFormatter::setForceDisableQRCode(bool b)
0105 {
0106     d->forceDisableQRCode = b;
0107 }
0108 
0109 bool GrantleeContactFormatter::forceDisableQRCode() const
0110 {
0111     return d->forceDisableQRCode;
0112 }
0113 
0114 void GrantleeContactFormatter::setShowQRCode(bool b)
0115 {
0116     d->showQRCode = b;
0117 }
0118 
0119 QString GrantleeContactFormatter::toHtml(HtmlForm form) const
0120 {
0121     if (!d->mErrorMessage.isEmpty()) {
0122         return d->mErrorMessage;
0123     }
0124 
0125     KContacts::Addressee rawContact;
0126     const Akonadi::Item localItem = item();
0127     if (localItem.isValid() && localItem.hasPayload<KContacts::Addressee>()) {
0128         rawContact = localItem.payload<KContacts::Addressee>();
0129     } else {
0130         rawContact = contact();
0131     }
0132 
0133     if (rawContact.isEmpty()) {
0134         return {};
0135     }
0136 
0137     // Custom fields
0138     QVariantList customFields;
0139     QVariantList customFieldsUrl;
0140     static QSet<QString> blacklistedKeys = {QStringLiteral("CRYPTOPROTOPREF"),
0141                                             QStringLiteral("OPENPGPFP"),
0142                                             QStringLiteral("SMIMEFP"),
0143                                             QStringLiteral("CRYPTOSIGNPREF"),
0144                                             QStringLiteral("CRYPTOENCRYPTPREF"),
0145                                             QStringLiteral("Anniversary"),
0146                                             QStringLiteral("BlogFeed"),
0147                                             QStringLiteral("Profession"),
0148                                             QStringLiteral("Office"),
0149                                             QStringLiteral("ManagersName"),
0150                                             QStringLiteral("AssistantsName"),
0151                                             QStringLiteral("SpousesName"),
0152                                             QStringLiteral("IMAddress"),
0153                                             QStringLiteral("AddressBook"),
0154                                             QStringLiteral("MailPreferedFormatting"),
0155                                             QStringLiteral("MailAllowToRemoteContent"),
0156                                             QStringLiteral("MAILPREFEREDFORMATTING"),
0157                                             QStringLiteral("MAILALLOWTOREMOTECONTENT")};
0158 
0159     const auto customs = rawContact.customs();
0160     for (QString custom : customs) {
0161         if (custom.startsWith(QLatin1StringView("KADDRESSBOOK-"))) {
0162             custom.remove(QStringLiteral("KADDRESSBOOK-X-"));
0163             custom.remove(QStringLiteral("KADDRESSBOOK-"));
0164 
0165             int pos = custom.indexOf(QLatin1Char(':'));
0166             QString key = custom.left(pos);
0167             QString value = custom.mid(pos + 1);
0168 
0169             if (blacklistedKeys.contains(key)) {
0170                 continue;
0171             }
0172 
0173             bool addUrl = false;
0174             // check whether it is a custom local field
0175             for (int i = 0; i < customFieldDescriptions().size(); ++i) {
0176                 const QVariantMap description = customFieldDescriptions().at(i);
0177                 if (description.value(QStringLiteral("key")).toString() == key) {
0178                     key = description.value(QStringLiteral("title")).toString();
0179                     const QString descriptionType = description.value(QStringLiteral("type")).toString();
0180                     if (descriptionType == QLatin1StringView("boolean")) {
0181                         if (value == QLatin1StringView("true")) {
0182                             value = i18nc("Boolean value", "yes");
0183                         } else {
0184                             value = i18nc("Boolean value", "no");
0185                         }
0186                     } else if (descriptionType == QLatin1StringView("date")) {
0187                         const QDate date = QDate::fromString(value, Qt::ISODate);
0188                         value = QLocale().toString(date, QLocale::ShortFormat);
0189                     } else if (descriptionType == QLatin1StringView("time")) {
0190                         const QTime time = QTime::fromString(value, Qt::ISODate);
0191                         value = QLocale::system().toString(time, QLocale::ShortFormat);
0192                     } else if (descriptionType == QLatin1StringView("datetime")) {
0193                         const QDateTime dateTime = QDateTime::fromString(value, Qt::ISODate);
0194                         value = QLocale().toString(dateTime, QLocale::ShortFormat);
0195                     } else if (descriptionType == QLatin1StringView("url")) {
0196                         value = KStringHandler::tagUrls(value.toHtmlEscaped());
0197                         addUrl = true;
0198                     }
0199                     break;
0200                 }
0201             }
0202             QVariantHash customFieldObject;
0203             customFieldObject.insert(QStringLiteral("title"), key);
0204             customFieldObject.insert(QStringLiteral("value"), value);
0205 
0206             if (addUrl) {
0207                 customFieldsUrl.append(customFieldObject);
0208             } else {
0209                 customFields.append(customFieldObject);
0210             }
0211         }
0212     }
0213 
0214     QVariantHash colorsObject;
0215 
0216     colorsObject.insert(QStringLiteral("linkColor"), KColorScheme(QPalette::Active, KColorScheme::View).foreground().color().name());
0217 
0218     colorsObject.insert(QStringLiteral("textColor"), KColorScheme(QPalette::Active, KColorScheme::View).foreground().color().name());
0219 
0220     colorsObject.insert(QStringLiteral("backgroundColor"), KColorScheme(QPalette::Active, KColorScheme::View).background().color().name());
0221 
0222     QVariantHash mapping;
0223     mapping.insert(QStringLiteral("contact"), QVariant::fromValue(ContactGrantleeWrapper(rawContact)));
0224     mapping.insert(QStringLiteral("colors"), colorsObject);
0225     mapping.insert(QStringLiteral("customFields"), customFields);
0226     mapping.insert(QStringLiteral("customFieldsUrl"), customFieldsUrl);
0227     mapping.insert(QStringLiteral("hasqrcode"), !d->forceDisableQRCode && d->showQRCode);
0228     KTextTemplate::Context context(mapping);
0229     context.setLocalizer(d->mEngine->localizer());
0230 
0231     if (form == SelfcontainedForm) {
0232         return d->mSelfcontainedTemplate->render(&context);
0233     } else if (form == EmbeddableForm) {
0234         return d->mEmbeddableTemplate->render(&context);
0235     } else {
0236         return {};
0237     }
0238 }
0239 
0240 void GrantleeContactFormatter::setApplicationDomain(const QByteArray &domain)
0241 {
0242     d->mEngine->localizer()->setApplicationDomain(domain);
0243 }