cutelyst  3.7.0
A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.
clearsilver.cpp
1 /*
2  * SPDX-FileCopyrightText: (C) 2013-2022 Daniel Nicoletti <dantti12@gmail.com>
3  * SPDX-License-Identifier: BSD-3-Clause
4  */
5 #include "clearsilver_p.h"
6 
7 #include "context.h"
8 #include "action.h"
9 #include "response.h"
10 
11 #include <QString>
12 #include <QFile>
13 #include <QtCore/QLoggingCategory>
14 
15 Q_LOGGING_CATEGORY(CUTELYST_CLEARSILVER, "cutelyst.clearsilver", QtWarningMsg)
16 
17 using namespace Cutelyst;
18 
19 ClearSilver::ClearSilver(QObject *parent, const QString &name) : View(new ClearSilverPrivate, parent, name)
20 {
21 
22 }
23 
24 QStringList ClearSilver::includePaths() const
25 {
26  Q_D(const ClearSilver);
27  return d->includePaths;
28 }
29 
30 void ClearSilver::setIncludePaths(const QStringList &paths)
31 {
32  Q_D(ClearSilver);
33  d->includePaths = paths;
34  Q_EMIT changed();
35 }
36 
37 QString ClearSilver::templateExtension() const
38 {
39  Q_D(const ClearSilver);
40  return d->extension;
41 }
42 
43 void ClearSilver::setTemplateExtension(const QString &extension)
44 {
45  Q_D(ClearSilver);
46  d->extension = extension;
47  Q_EMIT changed();
48 }
49 
50 QString ClearSilver::wrapper() const
51 {
52  Q_D(const ClearSilver);
53  return d->wrapper;
54 }
55 
56 void ClearSilver::setWrapper(const QString &name)
57 {
58  Q_D(ClearSilver);
59  d->wrapper = name;
60  Q_EMIT changed();
61 }
62 
63 NEOERR* cutelyst_render(void *user, char *data)
64 {
65  QByteArray *body = static_cast<QByteArray*>(user);
66  if (body) {
67  body->append(data);
68  }
69 // qDebug() << "_render" << body << data;
70  return nullptr;
71 }
72 
73 QByteArray ClearSilver::render(Context *c) const
74 {
75  Q_D(const ClearSilver);
76 
77  QByteArray output;
78  const QVariantHash &stash = c->stash();
79  QString templateFile = stash.value(QStringLiteral("template")).toString();
80  if (templateFile.isEmpty()) {
81  if (c->action() && !c->action()->reverse().isEmpty()) {
82  templateFile = c->action()->reverse() + d->extension;
83  }
84 
85  if (templateFile.isEmpty()) {
86  c->error(QStringLiteral("Cannot render template, template name or template stash key not defined"));
87  return output;
88  }
89  }
90 
91  qCDebug(CUTELYST_CLEARSILVER) << "Rendering template" <<templateFile;
92  QByteArray body;
93  if (!d->render(c, templateFile, stash, body)) {
94  return output;
95  }
96 
97  if (!d->wrapper.isEmpty()) {
98  QString wrapperFile = d->wrapper;
99 
100  QVariantHash data = stash;
101  data.insert(QStringLiteral("content"), body);
102  body.clear();
103 
104  if (!d->render(c, wrapperFile, data, body)) {
105  return output;
106  }
107  }
108  output = body;
109 
110  return output;
111 }
112 
113 NEOERR* findFile(void *c, HDF *hdf, const char *filename, char **contents)
114 {
115  Q_UNUSED(hdf)
116  const ClearSilverPrivate *priv = static_cast<ClearSilverPrivate*>(c);
117  if (!priv) {
118  return nerr_raise(NERR_NOMEM, "Cound not cast ClearSilverPrivate");
119  }
120 
121  for (const QString &includePath : priv->includePaths) {
122  QFile file(includePath + QLatin1Char('/') + QString::fromLatin1(filename));
123 
124  if (file.exists()) {
125  if (!file.open(QFile::ReadOnly)) {
126  return nerr_raise(NERR_IO, "Cound not open file: %s", file.errorString().toLatin1().data());
127  }
128 
129  *contents = qstrdup(file.readAll().constData());
130  qCDebug(CUTELYST_CLEARSILVER) << "Rendering template:" << file.fileName();
131  return nullptr;
132  }
133  }
134 
135  return nerr_raise(NERR_NOT_FOUND, "Cound not find file: %s", filename);
136 }
137 
138 bool ClearSilverPrivate::render(Context *c, const QString &filename, const QVariantHash &stash, QByteArray &output) const
139 {
140  HDF *hdf = hdfForStash(c, stash);
141  CSPARSE *cs;
142  NEOERR *error;
143 
144  error = cs_init(&cs, hdf);
145  if (error) {
146  STRING msg;
147  string_init(&msg);
148  nerr_error_traceback(error, &msg);
149  QString errorMsg;
150  errorMsg = QStringLiteral("Failed to init ClearSilver:\n+1").arg(QString::fromLatin1(msg.buf, msg.len));
151  renderError(c, errorMsg);
152 
153  string_clear(&msg);
154  hdf_destroy(&hdf);
155  nerr_ignore(&error);
156  return false;
157  }
158 
159  cs_register_fileload(cs, const_cast<ClearSilverPrivate*>(this), findFile);
160 
161  error = cs_parse_file(cs, filename.toLatin1().data());
162  if (error) {
163  STRING msg;
164  string_init(&msg);
165  nerr_error_traceback(error, &msg);
166  QString errorMsg;
167  errorMsg = QStringLiteral("Failed to parse template file: +1\n+2").arg(filename, QString::fromLatin1(msg.buf, msg.len));
168  renderError(c, errorMsg);
169  nerr_log_error(error);
170 
171  string_clear(&msg);
172  hdf_destroy(&hdf);
173  nerr_ignore(&error);
174  return false;
175  }
176 
177  cs_render(cs, &output, cutelyst_render);
178 
179  cs_destroy(&cs);
180  hdf_destroy(&hdf);
181 
182  return true;
183 }
184 
185 void ClearSilverPrivate::renderError(Context *c, const QString &error) const
186 {
187  c->error(error);
188  c->res()->setBody(error);
189 }
190 
191 HDF *ClearSilverPrivate::hdfForStash(Context *c, const QVariantHash &stash) const
192 {
193  HDF *hdf = 0;
194  hdf_init(&hdf);
195 
196  serializeHash(hdf, stash);
197 
198  const QMetaObject *meta = c->metaObject();
199  for (int i = 0; i < meta->propertyCount(); ++i) {
200  QMetaProperty prop = meta->property(i);
201  QString name = QLatin1String("c.") + QString::fromLatin1(prop.name());
202  QVariant value = prop.read(c);
203  serializeVariant(hdf, value, name);
204  }
205  return hdf;
206 }
207 
208 void ClearSilverPrivate::serializeHash(HDF *hdf, const QVariantHash &hash, const QString &prefix) const
209 {
210  QString _prefix;
211  if (!prefix.isEmpty()) {
212  _prefix = prefix + QLatin1Char('.');
213  }
214 
215  auto it = hash.constBegin();
216  while (it != hash.constEnd()) {
217  serializeVariant(hdf, it.value(), _prefix + it.key());
218  ++it;
219  }
220 }
221 
222 void ClearSilverPrivate::serializeMap(HDF *hdf, const QVariantMap &map, const QString &prefix) const
223 {
224  QString _prefix;
225  if (!prefix.isEmpty()) {
226  _prefix = prefix + QLatin1Char('.');
227  }
228 
229  auto it = map.constBegin();
230  while (it != map.constEnd()) {
231  serializeVariant(hdf, it.value(), _prefix + it.key());
232  ++it;
233  }
234 }
235 
236 void ClearSilverPrivate::serializeVariant(HDF *hdf, const QVariant &value, const QString &key) const
237 {
238 // qDebug() << key;
239 
240  switch (value.type()) {
241  case QVariant::String:
242  hdf_set_value(hdf, key.toLatin1().data(), value.toString().toLatin1().data());
243  break;
244  case QVariant::Int:
245  hdf_set_int_value(hdf, key.toLatin1().data(), value.toInt());
246  break;
247  case QVariant::Hash:
248  serializeHash(hdf, value.toHash(), key);
249  break;
250  case QVariant::Map:
251  serializeMap(hdf, value.toMap(), key);
252  break;
253  default:
254  if (value.canConvert(QMetaType::QString)) {
255  hdf_set_value(hdf, key.toLatin1().data(), value.toString().toLatin1().data());
256  }
257  break;
258  }
259 }
260 
261 #include "moc_clearsilver.cpp"
void setTemplateExtension(const QString &extension)
Sets the template extension, defaults to ".html".
Definition: clearsilver.cpp:43
void setWrapper(const QString &name)
Sets the template wrapper name, the template will be rendered into content variable in which the wrap...
Definition: clearsilver.cpp:56
ClearSilver(QObject *parent=nullptr, const QString &name=QString())
Constructs a ClearSilver object with the given parent and name.
Definition: clearsilver.cpp:19
void setIncludePaths(const QStringList &paths)
Sets the list of include paths which will be looked for when resolving templates files.
Definition: clearsilver.cpp:30
QByteArray render(Context *c) const final
Definition: clearsilver.cpp:73
QString name() const
Definition: component.cpp:31
QString reverse() const
Definition: component.cpp:43
The Cutelyst Context.
Definition: context.h:39
void stash(const QVariantHash &unite)
Definition: context.cpp:546
Response * res() const noexcept
Definition: context.cpp:103
bool error() const noexcept
Returns true if an error was set.
Definition: context.cpp:50
void setBody(QIODevice *body)
Definition: response.cpp:101
Cutelyst View abstract view component
Definition: view.h:22
The Cutelyst namespace holds all public Cutelyst API.
Definition: Mainpage.dox:8