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