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