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
14Q_LOGGING_CATEGORY(CUTELYST_CLEARSILVER, "cutelyst.clearsilver", QtWarningMsg)
15
16using 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
62NEOERR *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
113NEOERR *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
139bool 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
191void ClearSilverPrivate::renderError(Context *c, const QString &error) const
192{
193 c->error(error);
194 c->res()->setBody(error);
195}
196
197HDF *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
214void 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
230void 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
244void 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"
QString wrapper() const
Returns the template wrapper.
void setTemplateExtension(const QString &extension)
Sets the template extension, defaults to ".html".
void setWrapper(const QString &name)
Sets the template wrapper name, the template will be rendered into content variable in which the wrap...
ClearSilver(QObject *parent=nullptr, const QString &name=QString())
Constructs a ClearSilver object with the given parent and name.
QStringList includePaths() const
Returns the list of include paths.
void setIncludePaths(const QStringList &paths)
Sets the list of include paths which will be looked for when resolving templates files.
QByteArray render(Context *c) const final
QString templateExtension() const
Returns the template extension.
QString name() const
Definition component.cpp:33
QString reverse() const
Definition component.cpp:45
The Cutelyst Context.
Definition context.h:39
void stash(const QVariantHash &unite)
Definition context.cpp:566
Response * res() const noexcept
Definition context.cpp:102
bool error() const noexcept
Returns true if an error was set.
Definition context.cpp:49
void setBody(QIODevice *body)
Definition response.cpp:100
View(QObject *parent, const QString &name)
Definition view.cpp:18
The Cutelyst namespace holds all public Cutelyst API.
Definition Mainpage.dox:8
QByteArray & append(char ch)
void clear()
char * data()
QMetaProperty property(int index) const const
int propertyCount() const const
const char * name() const const
QVariant read(const QObject *object) const const
QObject(QObject *parent)
Q_EMITQ_EMIT
virtual const QMetaObject * metaObject() const const
QObject * parent() const const
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
QString fromLatin1(const char *str, int size)
QString & insert(int position, QChar ch)
bool isEmpty() const const
QByteArray toLatin1() const const