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