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