Messagelib

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

KDE's Doxygen guidelines are available online.