Cutelyst  2.1.0
context.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 "context_p.h"
19 
20 #include "common.h"
21 #include "request.h"
22 #include "response.h"
23 #include "action.h"
24 #include "dispatcher.h"
25 #include "controller.h"
26 #include "application.h"
27 #include "stats.h"
28 #include "enginerequest.h"
29 
30 #include "config.h"
31 
32 #include <QUrl>
33 #include <QUrlQuery>
34 #include <QCoreApplication>
35 #include <QBuffer>
36 
37 using namespace Cutelyst;
38 
39 Context::Context(ContextPrivate *priv) : d_ptr(priv)
40 {
41 }
42 
44  d_ptr(new ContextPrivate(app, app->engine(), app->dispatcher(), app->plugins()))
45 {
46  auto req = new DummyRequest(this);
47  req->body = new QBuffer(this);
48  req->body->open(QBuffer::ReadWrite);
49  req->context = this;
50 
51  d_ptr->response = new Response(app->defaultHeaders(), req);
52  d_ptr->request = new Request(req);
53  d_ptr->request->d_ptr->engine = d_ptr->engine;
54 }
55 
56 Context::~Context()
57 {
58  delete d_ptr->request;
59  delete d_ptr->response;
60  delete d_ptr;
61 }
62 
63 bool Context::error() const
64 {
65  Q_D(const Context);
66  return !d->error.isEmpty();
67 }
68 
69 void Context::error(const QString &error)
70 {
71  Q_D(Context);
72  if (error.isEmpty()) {
73  d->error.clear();
74  } else {
75  d->error << error;
76  qCCritical(CUTELYST_CORE) << error;
77  }
78 }
79 
80 QStringList Context::errors() const
81 {
82  Q_D(const Context);
83  return d->error;
84 }
85 
86 bool Context::state() const
87 {
88  Q_D(const Context);
89  return d->state;
90 }
91 
93 {
94  Q_D(Context);
95  d->state = state;
96 }
97 
99 {
100  Q_D(const Context);
101  return d->engine;
102 }
103 
105 {
106  Q_D(const Context);
107  return d->app;
108 }
109 
111 {
112  Q_D(const Context);
113  return d->response;
114 }
115 
117 {
118  Q_D(const Context);
119  return d->response;
120 }
121 
122 Action *Context::action() const
123 {
124  Q_D(const Context);
125  return d->action;
126 }
127 
128 QString Context::actionName() const
129 {
130  Q_D(const Context);
131  return d->action->name();
132 }
133 
134 QString Context::ns() const
135 {
136  Q_D(const Context);
137  return d->action->ns();
138 }
139 
140 Request *Context::request() const
141 {
142  Q_D(const Context);
143  return d->request;
144 }
145 
146 Request *Context::req() const
147 {
148  Q_D(const Context);
149  return d->request;
150 }
151 
153 {
154  Q_D(const Context);
155  return d->dispatcher;
156 }
157 
158 QString Cutelyst::Context::controllerName() const
159 {
160  Q_D(const Context);
161  return QString::fromLatin1(d->action->controller()->metaObject()->className());
162 }
163 
165 {
166  Q_D(const Context);
167  return d->action->controller();
168 }
169 
170 Controller *Context::controller(const QString &name) const
171 {
172  Q_D(const Context);
173  return d->dispatcher->controllers().value(name);
174 }
175 
177 {
178  Q_D(const Context);
179  return d->view;
180 }
181 
182 View *Context::view(const QString &name) const
183 {
184  Q_D(const Context);
185  return d->app->view(name);
186 }
187 
188 bool Context::setCustomView(const QString &name)
189 {
190  Q_D(Context);
191  d->view = d->app->view(name);
192  return d->view;
193 }
194 
195 QVariantHash &Context::stash()
196 {
197  Q_D(Context);
198  return d->stash;
199 }
200 
201 QVariant Context::stash(const QString &key) const
202 {
203  Q_D(const Context);
204  return d->stash.value(key);
205 }
206 
207 void Context::setStash(const QString &key, const QVariant &value)
208 {
209  Q_D(Context);
210  d->stash.insert(key, value);
211 }
212 
213 void Context::setStash(const QString &key, const ParamsMultiMap &map)
214 {
215  Q_D(Context);
216  d->stash.insert(key, QVariant::fromValue(map));
217 }
218 
219 QStack<Component *> Context::stack() const
220 {
221  Q_D(const Context);
222  return d->stack;
223 }
224 
225 QUrl Context::uriFor(const QString &path, const QStringList &args, const ParamsMultiMap &queryValues) const
226 {
227  Q_D(const Context);
228 
229  QUrl uri = d->request->uri();
230 
231  QString _path = path;
232  if (_path.isEmpty()) {
233  // ns must NOT return a leading slash
234  _path = QLatin1Char('/') + d->action->controller()->ns();
235  } else if (!_path.startsWith(QLatin1Char('/'))) {
236  _path.prepend(QLatin1Char('/'));
237  }
238 
239  if (!args.isEmpty()) {
240  _path = _path + QLatin1Char('/') + args.join(QLatin1Char('/'));
241  }
242  uri.setPath(_path, QUrl::DecodedMode);
243 
244  QUrlQuery query;
245  if (!queryValues.isEmpty()) {
246  // Avoid a trailing '?'
247  if (queryValues.size()) {
248  auto it = queryValues.constBegin();
249  const auto end = queryValues.constEnd();
250  while (it != end) {
251  query.addQueryItem(it.key(), it.value());
252  ++it;
253  }
254  }
255  }
256  uri.setQuery(query);
257 
258  return uri;
259 }
260 
261 QUrl Context::uriFor(Action *action, const QStringList &captures, const QStringList &args, const ParamsMultiMap &queryValues) const
262 {
263  Q_D(const Context);
264 
265  QUrl uri;
266  Action *localAction = action;
267  if (!localAction) {
268  localAction = d->action;
269  }
270 
271  QStringList localArgs = args;
272  QStringList localCaptures = captures;
273 
274  Action *expandedAction = d->dispatcher->expandAction(this, action);
275  if (expandedAction->numberOfCaptures() > 0) {
276  while (localCaptures.size() < expandedAction->numberOfCaptures()
277  && localArgs.size()) {
278  localCaptures.append(localArgs.takeFirst());
279  }
280  } else {
281  QStringList localCapturesAux = localCaptures;
282  localCapturesAux.append(localArgs);
283  localArgs = localCapturesAux;
284  localCaptures = QStringList();
285  }
286 
287  const QString path = d->dispatcher->uriForAction(localAction, localCaptures);
288  if (path.isEmpty()) {
289  qCWarning(CUTELYST_CORE) << "Can not find action for" << localAction << localCaptures;
290  return uri;
291  }
292 
293  uri = uriFor(path, localArgs, queryValues);
294  return uri;
295 }
296 
297 QUrl Context::uriForAction(const QString &path, const QStringList &captures, const QStringList &args, const ParamsMultiMap &queryValues) const
298 {
299  Q_D(const Context);
300 
301  QUrl uri;
302  Action *action = d->dispatcher->getActionByPath(path);
303  if (!action) {
304  qCWarning(CUTELYST_CORE) << "Can not find action for" << path;
305  return uri;
306  }
307 
308  uri = uriFor(action, captures, args, queryValues);
309  return uri;
310 }
311 
312 bool Context::detached() const
313 {
314  Q_D(const Context);
315  return d->detached;
316 }
317 
319 {
320  Q_D(Context);
321  if (action) {
322  d->dispatcher->forward(this, action);
323  }
324  d->detached = true;
325 }
326 
328 {
329  Q_D(Context);
330  return d->dispatcher->forward(this, action);
331 }
332 
333 bool Context::forward(const QString &action)
334 {
335  Q_D(Context);
336  return d->dispatcher->forward(this, action);
337 }
338 
339 Action *Context::getAction(const QString &action, const QString &ns) const
340 {
341  Q_D(const Context);
342  return d->dispatcher->getAction(action, ns);
343 }
344 
345 QVector<Action *> Context::getActions(const QString &action, const QString &ns) const
346 {
347  Q_D(const Context);
348  return d->dispatcher->getActions(action, ns);
349 }
350 
351 QVector<Cutelyst::Plugin *> Context::plugins() const
352 {
353  Q_D(const Context);
354  return d->plugins;
355 }
356 
358 {
359  Q_D(Context);
360  Q_ASSERT_X(code, "Context::execute", "trying to execute a null Cutelyst::Component");
361 
362  static int recursion = qEnvironmentVariableIsSet("RECURSION") ? qEnvironmentVariableIntValue("RECURSION") : 1000;
363  if (d->stack.size() >= recursion) {
364  QString msg = QStringLiteral("Deep recursion detected (stack size %1) calling %2, %3")
365  .arg(QString::number(d->stack.size()), code->reverse(), code->name());
366  error(msg);
367  setState(false);
368  return false;
369  }
370 
371  bool ret;
372  d->stack.push(code);
373 
374  if (d->stats) {
375  const QString statsInfo = d->statsStartExecute(code);
376 
377  ret = code->execute(this);
378 
379  if (!statsInfo.isEmpty()) {
380  d->statsFinishExecute(statsInfo);
381  }
382  } else {
383  ret = code->execute(this);
384  }
385 
386  d->stack.pop();
387 
388  return ret;
389 }
390 
391 QLocale Context::locale() const
392 {
393  Q_D(const Context);
394  return d->locale;
395 }
396 
397 void Context::setLocale(const QLocale &locale)
398 {
399  Q_D(Context);
400  d->locale = locale;
401 }
402 
403 QVariant Context::config(const QString &key, const QVariant &defaultValue) const
404 {
405  Q_D(const Context);
406  return d->app->config(key, defaultValue);
407 }
408 
409 QVariantMap Context::config() const
410 {
411  Q_D(const Context);
412  return d->app->config();
413 }
414 
415 QString Context::translate(const char *context, const char *sourceText, const char *disambiguation, int n) const
416 {
417  Q_D(const Context);
418  return d->app->translate(d->locale, context, sourceText, disambiguation, n);
419 }
420 
421 QString ContextPrivate::statsStartExecute(Component *code)
422 {
423  QString actionName;
424  // Skip internal actions
425  if (code->name().startsWith(QLatin1Char('_'))) {
426  return actionName;
427  }
428 
429  actionName = code->reverse();
430 
431  if (qobject_cast<Action *>(code)) {
432  actionName.prepend(QLatin1Char('/'));
433  }
434 
435  if (stack.size() > 2) {
436  actionName = QLatin1String("-> ") + actionName;
437  actionName = actionName.rightJustified(actionName.size() + stack.size() - 2, QLatin1Char(' '));
438  }
439 
440  stats->profileStart(actionName);
441 
442  return actionName;
443 }
444 
445 void ContextPrivate::statsFinishExecute(const QString &statsInfo)
446 {
447  stats->profileEnd(statsInfo);
448 }
449 
450 #include "moc_context.cpp"
451 #include "moc_context_p.cpp"
Context(Application *app)
Constructs a new DUMMY Context object that is child of Application This currently is experimental to ...
Definition: context.cpp:43
bool execute(Context *c)
Definition: component.cpp:70
QMap< QString, QString > ParamsMultiMap
QUrl uriForAction(const QString &path, const QStringList &captures=QStringList(), const QStringList &args=QStringList(), const ParamsMultiMap &queryValues=ParamsMultiMap()) const
Definition: context.cpp:297
Response * res() const
Definition: context.cpp:116
QVariantMap config() const
QString name() const
Definition: component.cpp:39
View * customView() const
Definition: context.cpp:176
Action * action() const
bool setCustomView(const QString &name)
Definition: context.cpp:188
QVector< Action * > getActions(const QString &action, const QString &ns=QString()) const
Definition: context.cpp:345
Controller * controller() const
void setStash(const QString &key, const QVariant &value)
Definition: context.cpp:207
void detach(Action *action=nullptr)
Definition: context.cpp:318
bool error() const
Returns true if an error was set.
Definition: context.cpp:63
Action * getAction(const QString &action, const QString &ns=QString()) const
Definition: context.cpp:339
QStack< Component * > stack() const
Definition: context.cpp:219
QIODevice * body() const
Definition: request.cpp:194
The Cutelyst Component base class.
Definition: component.h:38
This class represents a Cutelyst Action.
Definition: action.h:47
Application * app() const
Definition: context.cpp:104
Headers & defaultHeaders()
Definition: application.cpp:94
Request * request() const
The Cutelyst Context.
Definition: context.h:50
bool forward(Component *component)
Definition: context.cpp:327
Cutelyst Controller base class
Definition: controller.h:102
QUrl uriFor(const QString &path=QString(), const QStringList &args=QStringList(), const ParamsMultiMap &queryValues=ParamsMultiMap()) const
Definition: context.cpp:225
QString controllerName() const
bool detached() const
Definition: context.cpp:312
The Cutelyst namespace holds all public Cutelyst API.
Definition: Mainpage.dox:7
Engine * engine() const
Definition: context.cpp:98
QString ns() const
bool execute(Component *code)
Definition: context.cpp:357
void setState(bool state)
Sets the state of the current executed action, setting to false will make the dispatcher skip non pro...
Definition: context.cpp:92
QStringList errors() const
Returns a list of errors that were defined.
Definition: context.cpp:80
QVariantHash & stash()
Definition: context.cpp:195
QLocale locale() const
Definition: context.cpp:391
bool state() const
Response * response() const
Definition: context.cpp:110
void setLocale(const QLocale &locale)
Definition: context.cpp:397
virtual qint8 numberOfCaptures() const
Definition: action.cpp:143
QString translate(const char *context, const char *sourceText, const char *disambiguation=nullptr, int n=-1) const
Definition: context.cpp:415
Cutelyst View abstract view component
Definition: view.h:33
QString reverse() const
Definition: component.cpp:51
The Cutelyst Application.
Definition: application.h:55
Dispatcher * dispatcher() const
Definition: context.cpp:152
The Cutelyst Engine.
Definition: engine.h:33
The Cutelyst Dispatcher.
Definition: dispatcher.h:40
View * view(const QString &name=QString()) const
Definition: context.cpp:182
QString actionName() const
Request * req() const
QVector< Plugin * > plugins() const
Definition: context.cpp:351