Cutelyst  1.11.0
grantleeview.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 "grantleeview_p.h"
19 
20 #include "application.h"
21 #include "context.h"
22 #include "action.h"
23 #include "response.h"
24 #include "config.h"
25 
26 #include <grantlee/qtlocalizer.h>
27 
28 #include <QString>
29 #include <QDirIterator>
30 #include <QtCore/QLoggingCategory>
31 
32 Q_LOGGING_CATEGORY(CUTELYST_GRANTLEE, "cutelyst.grantlee")
33 
34 using namespace Cutelyst;
35 
36 GrantleeView::GrantleeView(QObject *parent, const QString &name) : View(parent, name)
37  , d_ptr(new GrantleeViewPrivate)
38 {
39  Q_D(GrantleeView);
40 
41  d->loader = QSharedPointer<Grantlee::FileSystemTemplateLoader>(new Grantlee::FileSystemTemplateLoader);
42 
43  d->engine = new Grantlee::Engine(this);
44  d->engine->addTemplateLoader(d->loader);
45  d->engine->addPluginPath(QStringLiteral(CUTELYST_PLUGINS_DIR));
46  d->engine->addDefaultLibrary(QStringLiteral("grantlee_cutelyst"));
47 
48  auto app = qobject_cast<Application *>(parent);
49  if (app) {
50  // make sure templates can be found on the current directory
51  setIncludePaths({ app->config(QStringLiteral("root")).toString() });
52 
53  // If CUTELYST_VAR is set the template might have become
54  // {{ Cutelyst.req.base }} instead of {{ c.req.base }}
55  d->cutelystVar = app->config(QStringLiteral("CUTELYST_VAR"), QStringLiteral("c")).toString();
56  } else {
57  // make sure templates can be found on the current directory
58  setIncludePaths({ QDir::currentPath() });
59  }
60 }
61 
62 GrantleeView::~GrantleeView()
63 {
64  delete d_ptr;
65 }
66 
67 QStringList GrantleeView::includePaths() const
68 {
69  Q_D(const GrantleeView);
70  return d->includePaths;
71 }
72 
73 void GrantleeView::setIncludePaths(const QStringList &paths)
74 {
75  Q_D(GrantleeView);
76  d->loader->setTemplateDirs(paths);
77  d->includePaths = paths;
78 }
79 
80 QString GrantleeView::templateExtension() const
81 {
82  Q_D(const GrantleeView);
83  return d->extension;
84 }
85 
86 void GrantleeView::setTemplateExtension(const QString &extension)
87 {
88  Q_D(GrantleeView);
89  d->extension = extension;
90 }
91 
92 QString GrantleeView::wrapper() const
93 {
94  Q_D(const GrantleeView);
95  return d->wrapper;
96 }
97 
98 void GrantleeView::setWrapper(const QString &name)
99 {
100  Q_D(GrantleeView);
101  d->wrapper = name;
102 }
103 
104 void GrantleeView::setCache(bool enable)
105 {
106  Q_D(GrantleeView);
107 
108  if (enable != d->cache.isNull()) {
109  return; // already enabled
110  }
111 
112  delete d->engine;
113  d->engine = new Grantlee::Engine(this);
114 
115  if (enable) {
116  d->cache = QSharedPointer<Grantlee::CachingLoaderDecorator>(new Grantlee::CachingLoaderDecorator(d->loader));
117  d->engine->addTemplateLoader(d->cache);
118  } else {
119  d->cache.clear();
120  d->engine->addTemplateLoader(d->loader);
121  }
122 }
123 
124 Grantlee::Engine *GrantleeView::engine() const
125 {
126  Q_D(const GrantleeView);
127  return d->engine;
128 }
129 
131 {
132  Q_D(GrantleeView);
133 
134  if (!isCaching()) {
135  setCache(true);
136  }
137 
138  const auto includePaths = d->includePaths;
139  for (const QString &includePath : includePaths) {
140  QDirIterator it(includePath, {
141  QLatin1Char('*') + d->extension
142  },
143  QDir::Files | QDir::NoDotAndDotDot,
144  QDirIterator::Subdirectories);
145  while (it.hasNext()) {
146  QString path = it.next();
147  path.remove(includePath);
148  if (path.startsWith(QLatin1Char('/'))) {
149  path.remove(0, 1);
150  }
151 
152  if (d->cache->canLoadTemplate(path)) {
153  d->cache->loadByName(path, d->engine);
154  }
155  }
156  }
157 }
158 
160 {
161  Q_D(const GrantleeView);
162  return !d->cache.isNull();
163 }
164 
165 QByteArray GrantleeView::render(Context *c) const
166 {
167  Q_D(const GrantleeView);
168 
169  QByteArray ret;
170  c->setStash(d->cutelystVar, QVariant::fromValue(c));
171  const QVariantHash stash = c->stash();
172  auto it = stash.constFind(QStringLiteral("template"));
173  QString templateFile;
174  if (it != stash.constEnd()) {
175  templateFile = it.value().toString();
176  } else {
177  if (c->action() && !c->action()->reverse().isEmpty()) {
178  templateFile = c->action()->reverse() + d->extension;
179  if (templateFile.startsWith(QLatin1Char('/'))) {
180  templateFile.remove(0, 1);
181  }
182  }
183 
184  if (templateFile.isEmpty()) {
185  c->error(QStringLiteral("Cannot render template, template name or template stash key not defined"));
186  return ret;
187  }
188  }
189 
190  qCDebug(CUTELYST_GRANTLEE) << "Rendering template" << templateFile;
191 
192  Grantlee::Context gc(stash);
193 
194  auto localizer = QSharedPointer<Grantlee::QtLocalizer>::create(c->locale());
195 
196  auto transIt = d->translators.constFind(c->locale());
197  if (transIt != d->translators.constEnd()) {
198  localizer.data()->installTranslator(transIt.value(), transIt.key().name());
199  }
200 
201  auto catalogIt = d->translationCatalogs.constBegin();
202  while (catalogIt != d->translationCatalogs.constEnd()) {
203  localizer.data()->loadCatalog(catalogIt.value(), catalogIt.key());
204  ++it;
205  }
206 
207  gc.setLocalizer(localizer);
208 
209  Grantlee::Template tmpl = d->engine->loadByName(templateFile);
210  QString content = tmpl->render(&gc);
211  if (tmpl->error() != Grantlee::NoError) {
212  c->res()->setBody(c->translate("Cutelyst::GrantleeView", "Internal server error."));
213  c->error(QLatin1String("Error while rendering template: ") + tmpl->errorString());
214  return ret;
215  }
216 
217  if (!d->wrapper.isEmpty()) {
218  Grantlee::Template wrapper = d->engine->loadByName(d->wrapper);
219  Grantlee::SafeString safeContent(content, true);
220  gc.insert(QStringLiteral("content"), safeContent);
221  content = wrapper->render(&gc);
222 
223  if (wrapper->error() != Grantlee::NoError) {
224  c->res()->setBody(c->translate("Cutelyst::GrantleeView", "Internal server error."));
225  c->error(QLatin1String("Error while rendering template: ") + tmpl->errorString());
226  return ret;
227  }
228  }
229 
230  ret = content.toUtf8();
231  return ret;
232 }
233 
234 void GrantleeView::addTranslator(const QLocale &locale, QTranslator *translator)
235 {
236  Q_D(GrantleeView);
237  Q_ASSERT_X(translator, "add translator to GrantleeView", "invalid QTranslator object");
238  d->translators.insert(locale, translator);
239 }
240 
241 void GrantleeView::addTranslator(const QString &locale, QTranslator *translator)
242 {
243  addTranslator(QLocale(locale), translator);
244 }
245 
246 void GrantleeView::addTranslationCatalog(const QString &path, const QString &catalog)
247 {
248  Q_D(GrantleeView);
249  Q_ASSERT_X(!path.isEmpty(), "add translation catalog to GrantleeView", "empty path");
250  Q_ASSERT_X(!catalog.isEmpty(), "add translation catalog to GrantleeView", "empty catalog name");
251  d->translationCatalogs.insert(catalog, path);
252 }
253 
254 void GrantleeView::addTranslationCatalogs(const QHash<QString, QString> &catalogs)
255 {
256  Q_D(GrantleeView);
257  Q_ASSERT_X(!catalogs.empty(), "add translation catalogs to GranteleeView", "empty QHash");
258  d->translationCatalogs.unite(catalogs);
259 }
260 
261 #include "moc_grantleeview.cpp"
Response * res() const
Definition: context.cpp:117
void setIncludePaths(const QStringList &paths)
Sets the list of include paths which will be looked for when resolving templates files.
void setStash(const QString &key, const QVariant &value)
Definition: context.cpp:208
bool error() const
Returns true if an error was set.
Definition: context.cpp:64
QByteArray render(Context *c) const final
The Cutelyst Context.
Definition: context.h:50
QString wrapper() const
Returns the template wrapper.
QStringList includePaths() const
Returns the list of include paths.
bool isCaching() const
Returns true if caching is enabled.
The Cutelyst namespace holds all public Cutelyst API.
Definition: Mainpage.dox:7
void setWrapper(const QString &name)
Sets the template wrapper name, the template will be rendered into content variable in which the wrap...
QVariant config(const QString &key, const QVariant &defaultValue=QVariant()) const
void setTemplateExtension(const QString &extension)
Sets the template extension, defaults to ".html".
void addTranslationCatalogs(const QHash< QString, QString > &catalogs)
QLocale locale() const
Definition: context.cpp:392
QString translate(const char *context, const char *sourceText, const char *disambiguation=nullptr, int n=-1) const
Definition: context.cpp:422
QString templateExtension() const
Returns the template extension.
Cutelyst View abstract view component
Definition: view.h:33
The Cutelyst Application.
Definition: application.h:54
QString reverse() const
Definition: component.h:141
void addTranslator(const QLocale &locale, QTranslator *translator)
void setBody(QIODevice *body)
Definition: response.cpp:119
QString name() const
Definition: view.cpp:40
void addTranslationCatalog(const QString &path, const QString &catalog)
Grantlee::Engine * engine() const
void setCache(bool enable)
Sets if template caching should be done, this increases performance at the cost of higher memory usag...
void stash(const QVariantHash &unite)
Definition: context.h:499