cutelyst  3.9.1
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 "action.h"
6 #include "clearsilver_p.h"
7 #include "context.h"
8 #include "response.h"
9 
10 #include <QFile>
11 #include <QString>
12 #include <QtCore/QLoggingCategory>
13 
14 Q_LOGGING_CATEGORY(CUTELYST_CLEARSILVER, "cutelyst.clearsilver", QtWarningMsg)
15 
16 using namespace Cutelyst;
17 
19  : View(new ClearSilverPrivate, parent, name)
20 {
21 }
22 
24 {
25  Q_D(const ClearSilver);
26  return d->includePaths;
27 }
28 
30 {
31  Q_D(ClearSilver);
32  d->includePaths = paths;
33  Q_EMIT changed();
34 }
35 
37 {
38  Q_D(const ClearSilver);
39  return d->extension;
40 }
41 
43 {
44  Q_D(ClearSilver);
45  d->extension = extension;
46  Q_EMIT changed();
47 }
48 
50 {
51  Q_D(const ClearSilver);
52  return d->wrapper;
53 }
54 
56 {
57  Q_D(ClearSilver);
58  d->wrapper = name;
59  Q_EMIT changed();
60 }
61 
62 NEOERR *cutelyst_render(void *user, char *data)
63 {
64  QByteArray *body = static_cast<QByteArray *>(user);
65  if (body) {
66  body->append(data);
67  }
68  // qDebug() << "_render" << body << data;
69  return nullptr;
70 }
71 
73 {
74  Q_D(const ClearSilver);
75 
76  QByteArray output;
77  const QVariantHash &stash = c->stash();
78  QString templateFile = stash.value(QStringLiteral("template")).toString();
79  if (templateFile.isEmpty()) {
80  if (c->action() && !c->action()->reverse().isEmpty()) {
81  templateFile = c->action()->reverse() + d->extension;
82  }
83 
84  if (templateFile.isEmpty()) {
85  c->error(QStringLiteral(
86  "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(
127  NERR_IO, "Cound not open file: %s", file.errorString().toLatin1().data());
128  }
129 
130  *contents = qstrdup(file.readAll().constData());
131  qCDebug(CUTELYST_CLEARSILVER) << "Rendering template:" << file.fileName();
132  return nullptr;
133  }
134  }
135 
136  return nerr_raise(NERR_NOT_FOUND, "Cound not find file: %s", filename);
137 }
138 
139 bool ClearSilverPrivate::render(Context *c,
140  const QString &filename,
141  const QVariantHash &stash,
142  QByteArray &output) const
143 {
144  HDF *hdf = hdfForStash(c, stash);
145  CSPARSE *cs;
146  NEOERR *error;
147 
148  error = cs_init(&cs, hdf);
149  if (error) {
150  STRING msg;
151  string_init(&msg);
152  nerr_error_traceback(error, &msg);
153  QString errorMsg;
154  errorMsg = QStringLiteral("Failed to init ClearSilver:\n+1")
155  .arg(QString::fromLatin1(msg.buf, msg.len));
156  renderError(c, errorMsg);
157 
158  string_clear(&msg);
159  hdf_destroy(&hdf);
160  nerr_ignore(&error);
161  return false;
162  }
163 
164  cs_register_fileload(cs, const_cast<ClearSilverPrivate *>(this), findFile);
165 
166  error = cs_parse_file(cs, filename.toLatin1().data());
167  if (error) {
168  STRING msg;
169  string_init(&msg);
170  nerr_error_traceback(error, &msg);
171  QString errorMsg;
172  errorMsg = QStringLiteral("Failed to parse template file: +1\n+2")
173  .arg(filename, QString::fromLatin1(msg.buf, msg.len));
174  renderError(c, errorMsg);
175  nerr_log_error(error);
176 
177  string_clear(&msg);
178  hdf_destroy(&hdf);
179  nerr_ignore(&error);
180  return false;
181  }
182 
183  cs_render(cs, &output, cutelyst_render);
184 
185  cs_destroy(&cs);
186  hdf_destroy(&hdf);
187 
188  return true;
189 }
190 
191 void ClearSilverPrivate::renderError(Context *c, const QString &error) const
192 {
193  c->error(error);
194  c->res()->setBody(error);
195 }
196 
197 HDF *ClearSilverPrivate::hdfForStash(Context *c, const QVariantHash &stash) const
198 {
199  HDF *hdf = 0;
200  hdf_init(&hdf);
201 
202  serializeHash(hdf, stash);
203 
204  const QMetaObject *meta = c->metaObject();
205  for (int i = 0; i < meta->propertyCount(); ++i) {
206  QMetaProperty prop = meta->property(i);
207  QString name = QLatin1String("c.") + QString::fromLatin1(prop.name());
208  QVariant value = prop.read(c);
209  serializeVariant(hdf, value, name);
210  }
211  return hdf;
212 }
213 
214 void ClearSilverPrivate::serializeHash(HDF *hdf,
215  const QVariantHash &hash,
216  const QString &prefix) const
217 {
218  QString _prefix;
219  if (!prefix.isEmpty()) {
220  _prefix = prefix + QLatin1Char('.');
221  }
222 
223  auto it = hash.constBegin();
224  while (it != hash.constEnd()) {
225  serializeVariant(hdf, it.value(), _prefix + it.key());
226  ++it;
227  }
228 }
229 
230 void ClearSilverPrivate::serializeMap(HDF *hdf, const QVariantMap &map, const QString &prefix) const
231 {
232  QString _prefix;
233  if (!prefix.isEmpty()) {
234  _prefix = prefix + QLatin1Char('.');
235  }
236 
237  auto it = map.constBegin();
238  while (it != map.constEnd()) {
239  serializeVariant(hdf, it.value(), _prefix + it.key());
240  ++it;
241  }
242 }
243 
244 void ClearSilverPrivate::serializeVariant(HDF *hdf, const QVariant &value, const QString &key) const
245 {
246  // qDebug() << key;
247 
248  switch (value.type()) {
249  case QVariant::String:
250  hdf_set_value(hdf, key.toLatin1().data(), value.toString().toLatin1().data());
251  break;
252  case QVariant::Int:
253  hdf_set_int_value(hdf, key.toLatin1().data(), value.toInt());
254  break;
255  case QVariant::Hash:
256  serializeHash(hdf, value.toHash(), key);
257  break;
258  case QVariant::Map:
259  serializeMap(hdf, value.toMap(), key);
260  break;
261  default:
262  if (value.canConvert(QMetaType::QString)) {
263  hdf_set_value(hdf, key.toLatin1().data(), value.toString().toLatin1().data());
264  }
265  break;
266  }
267 }
268 
269 #include "moc_clearsilver.cpp"
void clear()
QString templateExtension() const
Returns the template extension.
bool error() const noexcept
Returns true if an error was set.
Definition: context.cpp:49
Response * res() const noexcept
Definition: context.cpp:102
QString wrapper() const
Returns the template wrapper.
virtual const QMetaObject * metaObject() const const
ClearSilver(QObject *parent=nullptr, const QString &name=QString())
Constructs a ClearSilver object with the given parent and name.
Definition: clearsilver.cpp:18
The Cutelyst Context.
Definition: context.h:38
int propertyCount() const const
QByteArray render(Context *c) const final
Definition: clearsilver.cpp:72
QString & insert(qsizetype position, QChar ch)
void stash(const QVariantHash &unite)
Definition: context.cpp:566
QString name() const
Definition: component.cpp:33
bool isEmpty() const const
The Cutelyst namespace holds all public Cutelyst API.
Definition: Mainpage.dox:7
QString reverse() const
Definition: component.cpp:45
QVariant read(const QObject *object) const const
QByteArray & append(QByteArrayView data)
void setTemplateExtension(const QString &extension)
Sets the template extension, defaults to ".html".
Definition: clearsilver.cpp:42
const char * name() const const
QStringList includePaths() const
Returns the list of include paths.
QString fromLatin1(QByteArrayView str)
QByteArray toLatin1() const const
void setIncludePaths(const QStringList &paths)
Sets the list of include paths which will be looked for when resolving templates files.
Definition: clearsilver.cpp:29
Cutelyst View abstract view component
Definition: view.h:21
char * data()
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:55
void setBody(QIODevice *body)
Definition: response.cpp:100
QString arg(Args &&... args) const const
Q_EMITQ_EMIT
QMetaProperty property(int index) const const