Messagelib

messagepartrendererfactory.cpp
1/*
2 This file is part of KMail, the KDE mail client.
3 SPDX-FileCopyrightText: 2017 Sandro KnauƟ <sknauss@kde.org>
4
5 SPDX-License-Identifier: GPL-2.0-or-later
6*/
7
8#include "messagepartrendererfactory.h"
9#include "messagepartrendererbase.h"
10#include "messagepartrendererfactory_p.h"
11#include "messagepartrenderplugin.h"
12#include "messageviewer_debug.h"
13#include "viewer/urlhandlermanager.h"
14
15#include "plugins/attachmentmessagepartrenderer.h"
16#include "plugins/messagepartrenderer.h"
17#include "plugins/textmessagepartrenderer.h"
18
19#include <KPluginMetaData>
20#include <QJsonArray>
21#include <QMimeDatabase>
22#include <QPluginLoader>
23#include <algorithm>
24
25using namespace MessageViewer;
26
27MessagePartRendererFactoryPrivate::~MessagePartRendererFactoryPrivate()
28{
30 while (i.hasNext()) {
31 i.next();
32 auto renderInfo = i.value();
33 renderInfo.erase(renderInfo.begin(), renderInfo.end());
34 }
35}
36
37void MessagePartRendererFactoryPrivate::setup()
38{
39 if (m_renderers.isEmpty()) {
40 initialize_builtin_renderers();
41 loadPlugins();
42 }
43 Q_ASSERT(!m_renderers.isEmpty());
44}
45
46void MessagePartRendererFactoryPrivate::loadPlugins()
47{
48 if (m_pluginSubdir.isEmpty()) {
49 return;
50 }
51 const QList<KPluginMetaData> plugins = KPluginMetaData::findPlugins(m_pluginSubdir);
52 for (const auto &md : plugins) {
53 const auto pluginData = md.rawData().value(QLatin1StringView("renderer")).toArray();
54 if (pluginData.isEmpty()) {
55 qCWarning(MESSAGEVIEWER_LOG) << "Plugin" << md.fileName() << "has no meta data.";
56 continue;
57 }
58 QPluginLoader loader(md.fileName());
59 auto plugin = qobject_cast<MessagePartRenderPlugin *>(loader.instance());
60 if (!plugin) {
61 qCWarning(MESSAGEVIEWER_LOG) << md.fileName() << "is not a MessagePartRendererPlugin";
62 continue;
63 }
64
65 MessagePartRendererBase *renderer = nullptr;
66 for (int i = 0; (renderer = plugin->renderer(i)) && i < pluginData.size(); ++i) {
67 const auto metaData = pluginData.at(i).toObject();
68 const auto type = metaData.value(QLatin1StringView("type")).toString().toUtf8();
69 if (type.isEmpty()) {
70 qCWarning(MESSAGEVIEWER_LOG) << md.fileName() << "returned empty type specification for index" << i;
71 break;
72 }
73 const auto mimetype = metaData.value(QLatin1StringView("mimetype")).toString().toLower();
74 // priority should always be higher than the built-in ones, otherwise what's the point?
75 const auto priority = metaData.value(QLatin1StringView("priority")).toInt() + 100;
76 qCDebug(MESSAGEVIEWER_LOG) << "renderer plugin for " << type << mimetype << priority;
77 insert(type, renderer, mimetype, priority);
78 }
79
80 const Interface::BodyPartURLHandler *handler = nullptr;
81 for (int i = 0; (handler = plugin->urlHandler(i)); ++i) {
82 const auto metaData = pluginData.at(i).toObject();
83 const auto mimeType = metaData.value(QLatin1StringView("mimetype")).toString().toLower();
84 URLHandlerManager::instance()->registerHandler(handler, mimeType);
85 }
86 }
87}
88
89void MessagePartRendererFactoryPrivate::initialize_builtin_renderers()
90{
91 insert("MimeTreeParser::MessagePart", new MessagePartRenderer());
92 insert("MimeTreeParser::TextMessagePart", new TextMessagePartRenderer());
93 insert("MimeTreeParser::AttachmentMessagePart", new AttachmentMessagePartRenderer());
94}
95
96void MessagePartRendererFactoryPrivate::insert(const QByteArray &type, MessagePartRendererBase *renderer, const QString &mimeType, int priority)
97{
98 if (type.isEmpty() || !renderer) {
99 return;
100 }
101
102 QMimeDatabase db;
103 const auto mt = db.mimeTypeForName(mimeType);
104
105 RendererInfo info;
106 info.renderer.reset(renderer);
107 info.mimeType = mt.isValid() ? mt.name() : mimeType;
108 info.priority = priority;
109
110 auto &v = m_renderers[type];
111 v.push_back(info);
112}
113
114MessagePartRendererFactory::MessagePartRendererFactory()
115 : d(new MessagePartRendererFactoryPrivate)
116{
117}
118
119MessagePartRendererFactory::~MessagePartRendererFactory() = default;
120
122{
123 d->m_pluginSubdir = subdir;
124}
125
126MessagePartRendererFactory *MessagePartRendererFactory::instance()
127{
128 static MessagePartRendererFactory s_instance;
129 return &s_instance;
130}
131
132QList<MessagePartRendererBase *> MessagePartRendererFactory::renderersForPart(const QMetaObject *mo, const MimeTreeParser::MessagePartPtr &mp) const
133{
134 d->setup();
135
136 const auto mtName = mp->content() ? QString::fromUtf8(mp->content()->contentType()->mimeType().toLower()) : QString();
137 QMimeDatabase db;
138 const auto mt = db.mimeTypeForName(mtName);
139 auto ancestors = mt.allAncestors();
140 if (mt.isValid() || !mtName.isEmpty()) {
141 ancestors.prepend(mt.isValid() ? mt.name() : mtName);
142 }
143
144 auto candidates = d->m_renderers.value(mo->className());
145
146 // remove candidates with a mimetype set that don't match the mimetype of the part
147 candidates.erase(std::remove_if(candidates.begin(),
148 candidates.end(),
149 [ancestors](const RendererInfo &info) {
150 if (info.mimeType.isEmpty()) {
151 return false;
152 }
153 return !ancestors.contains(info.mimeType);
154 }),
155 candidates.end());
156
157 // sort most specific mimetpypes first
158 std::stable_sort(candidates.begin(), candidates.end(), [ancestors](const RendererInfo &lhs, const RendererInfo &rhs) {
159 if (lhs.mimeType == rhs.mimeType) {
160 return lhs.priority > rhs.priority;
161 }
162 if (lhs.mimeType.isEmpty()) {
163 return false;
164 }
165 if (rhs.mimeType.isEmpty()) {
166 return true;
167 }
168 return ancestors.indexOf(lhs.mimeType) < ancestors.indexOf(rhs.mimeType);
169 });
170
172 r.reserve(candidates.size());
173 for (const auto &candidate : candidates) {
174 r.push_back(candidate.renderer.data());
175 }
176 return r;
177}
static QList< KPluginMetaData > findPlugins(const QString &directory, std::function< bool(const KPluginMetaData &)> filter={}, KPluginMetaDataOptions options={})
An interface to body part reader link handlers.
The MessagePartRendererBase class.
The MessagePartRendererFactory class.
void setPluginPath(const QString &subdir)
Customize where to look for render plugins.
char * toString(const EngineQuery &query)
KCALUTILS_EXPORT QString mimeType()
KIOCORE_EXPORT MimetypeJob * mimetype(const QUrl &url, JobFlags flags=DefaultFlags)
KGuiItem insert()
void reserve(int alloc)
const char * className() const const
QMimeType mimeTypeForName(const QString &nameOrAlias) const const
bool isValid() const const
QString fromUtf8(const char *str, int size)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sun Feb 25 2024 18:37:31 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.