Messagelib

messagepartrendererfactory.cpp
1 /*
2  This file is part of KMail, the KDE mail client.
3  SPDX-FileCopyrightText: 2017 Sandro Knauß <skna[email protected]>
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 
25 using namespace MessageViewer;
26 
27 MessagePartRendererFactoryPrivate::~MessagePartRendererFactoryPrivate()
28 {
30  while (i.hasNext()) {
31  i.next();
32  auto renderInfo = i.value();
33  renderInfo.erase(renderInfo.begin(), renderInfo.end());
34  }
35 }
36 
37 void MessagePartRendererFactoryPrivate::setup()
38 {
39  if (m_renderers.isEmpty()) {
40  initialize_builtin_renderers();
41  loadPlugins();
42  }
43  Q_ASSERT(!m_renderers.isEmpty());
44 }
45 
46 void MessagePartRendererFactoryPrivate::loadPlugins()
47 {
48  if (m_pluginSubdir.isEmpty()) {
49  return;
50  }
51  const QVector<KPluginMetaData> plugins = KPluginMetaData::findPlugins(m_pluginSubdir);
52  for (const auto &md : plugins) {
53  const auto pluginData = md.rawData().value(QLatin1String("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(QLatin1String("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(QLatin1String("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(QLatin1String("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(QLatin1String("mimetype")).toString().toLower();
84  URLHandlerManager::instance()->registerHandler(handler, mimeType);
85  }
86  }
87 }
88 
89 void 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 
96 void 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 
114 MessagePartRendererFactory::MessagePartRendererFactory()
115  : d(new MessagePartRendererFactoryPrivate)
116 {
117 }
118 
119 MessagePartRendererFactory::~MessagePartRendererFactory() = default;
120 
122 {
123  d->m_pluginSubdir = subdir;
124 }
125 
126 MessagePartRendererFactory *MessagePartRendererFactory::instance()
127 {
128  static MessagePartRendererFactory s_instance;
129  return &s_instance;
130 }
131 
132 QVector<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 }
QString fromUtf8(const char *str, int size)
The MessagePartRendererBase class.
KCALUTILS_EXPORT QString mimeType()
void setPluginPath(const QString &subdir)
Customize where to look for render plugins.
KIOCORE_EXPORT MimetypeJob * mimetype(const QUrl &url, JobFlags flags=DefaultFlags)
QMimeType mimeTypeForName(const QString &nameOrAlias) const const
char * toString(const T &value)
void push_back(QChar ch)
bool isValid() const const
void reserve(int size)
The MessagePartRendererFactory class.
static QVector< KPluginMetaData > findPlugins(const QString &directory, std::function< bool(const KPluginMetaData &)> filter, KPluginMetaDataOption option)
const char * className() const const
KGuiItem insert()
An interface to body part reader link handlers.
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Sun Mar 26 2023 04:08:11 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.