cutelyst 3.9.1
A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.
application.cpp
1/*
2 * SPDX-FileCopyrightText: (C) 2013-2022 Daniel Nicoletti <dantti12@gmail.com>
3 * SPDX-License-Identifier: BSD-3-Clause
4 */
5#include "application_p.h"
6#include "common.h"
7#include "config.h"
8#include "context_p.h"
9#include "controller.h"
10#include "controller_p.h"
11#include "dispatchtype.h"
12#include "enginerequest.h"
13#include "request.h"
14#include "request_p.h"
15#include "response.h"
16#include "response_p.h"
17#include "stats.h"
18#include "utils.h"
19#include "view.h"
20
21#include <QtCore/QCoreApplication>
22#include <QtCore/QDataStream>
23#include <QtCore/QDir>
24#include <QtCore/QFileInfo>
25#include <QtCore/QLocale>
26#include <QtCore/QPluginLoader>
27#include <QtCore/QStringList>
28#include <QtCore/QTranslator>
29
30Q_LOGGING_CATEGORY(CUTELYST_DISPATCHER, "cutelyst.dispatcher", QtWarningMsg)
31Q_LOGGING_CATEGORY(CUTELYST_DISPATCHER_PATH, "cutelyst.dispatcher.path", QtWarningMsg)
32Q_LOGGING_CATEGORY(CUTELYST_DISPATCHER_CHAINED, "cutelyst.dispatcher.chained", QtWarningMsg)
33Q_LOGGING_CATEGORY(CUTELYST_CONTROLLER, "cutelyst.controller", QtWarningMsg)
34Q_LOGGING_CATEGORY(CUTELYST_CORE, "cutelyst.core", QtWarningMsg)
35Q_LOGGING_CATEGORY(CUTELYST_ENGINE, "cutelyst.engine", QtWarningMsg)
36Q_LOGGING_CATEGORY(CUTELYST_UPLOAD, "cutelyst.upload", QtWarningMsg)
37Q_LOGGING_CATEGORY(CUTELYST_MULTIPART, "cutelyst.multipart", QtWarningMsg)
38Q_LOGGING_CATEGORY(CUTELYST_VIEW, "cutelyst.view", QtWarningMsg)
39Q_LOGGING_CATEGORY(CUTELYST_REQUEST, "cutelyst.request", QtWarningMsg)
40Q_LOGGING_CATEGORY(CUTELYST_RESPONSE, "cutelyst.response", QtWarningMsg)
41Q_LOGGING_CATEGORY(CUTELYST_STATS, "cutelyst.stats", QtWarningMsg)
42Q_LOGGING_CATEGORY(CUTELYST_COMPONENT, "cutelyst.component", QtWarningMsg)
43
44using namespace Cutelyst;
45
48 , d_ptr(new ApplicationPrivate)
49{
50 Q_D(Application);
51
52 d->q_ptr = this;
53
54 qRegisterMetaType<ParamsMultiMap>();
55#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
56 qRegisterMetaTypeStreamOperators<ParamsMultiMap>("ParamsMultiMap");
57#endif
58
59 d->dispatcher = new Dispatcher(this);
60
61 loadTranslations(QStringLiteral("cutelystcore"));
62}
63
64Application::~Application()
65{
66 delete d_ptr;
67}
68
70{
71 qCDebug(CUTELYST_CORE) << "Default Application::init called on pid:"
73 return true;
74}
75
77{
78 qCDebug(CUTELYST_CORE) << "Default Application::postFork called on pid:"
80 return true;
81}
82
84{
85 Q_D(Application);
86 return d->headers;
87}
88
90{
91 Q_D(Application);
92 d->headers.setHeader(QStringLiteral("X_CUTELYST"), QStringLiteral(VERSION));
93}
94
96{
97 Q_D(Application);
98 if (d->plugins.contains(plugin)) {
99 return false;
100 }
101 d->plugins.append(plugin);
102 return true;
103}
104
106{
107 Q_D(Application);
108 const auto name = QString::fromLatin1(controller->metaObject()->className());
109 if (d->controllersHash.contains(name)) {
110 return false;
111 }
112 d->controllersHash.insert(name, controller);
113 d->controllers.append(controller);
114 return true;
115}
116
118{
119 Q_D(Application);
120 if (d->views.contains(view->name())) {
121 qCWarning(CUTELYST_CORE) << "Not registering View." << view->metaObject()->className()
122 << "There is already a view with this name:" << view->name();
123 return false;
124 }
125 d->views.insert(view->name(), view);
126 return true;
127}
128
130{
131 Q_D(Application);
132 if (d->dispatchers.contains(dispatcher)) {
133 return false;
134 }
135 d->dispatchers.append(dispatcher);
136 return true;
137}
138
140{
141 Q_D(Application);
142 auto it = d->factories.constFind(name);
143 if (it != d->factories.constEnd()) {
144 ComponentFactory *factory = it.value();
145 if (factory) {
146 return factory->createComponent(parent);
147 } else {
148 return nullptr;
149 }
150 }
151
152 const QByteArrayList dirs = QByteArrayList{QByteArrayLiteral(CUTELYST_PLUGINS_DIR)} +
153 qgetenv("CUTELYST_PLUGINS_DIR").split(';');
154 for (const QByteArray &dir : dirs) {
155 Component *component = d->createComponentPlugin(name, parent, QString::fromLocal8Bit(dir));
156 if (component) {
157 return component;
158 }
159 }
160 qCDebug(CUTELYST_CORE) << "Did not find plugin" << name << "on" << dirs << "for" << parent;
161
162 return nullptr;
163}
164
165const char *Application::cutelystVersion() noexcept
166{
167 return VERSION;
168}
169
171{
172 Q_D(const Application);
173 return d->controllers;
174}
175
176View *Application::view(const QString &name) const
177{
178 Q_D(const Application);
179 return d->views.value(name);
180}
181
183{
184 Q_D(const Application);
185 return d->views.value(name);
186}
187
188QVariant Application::config(const QString &key, const QVariant &defaultValue) const
189{
190 Q_D(const Application);
191 auto it = d->config.constFind(key);
192 if (it != d->config.constEnd()) {
193 return it.value();
194 }
195 return defaultValue;
196}
197
199{
200 Q_D(const Application);
201 return d->dispatcher;
202}
203
205{
206 Q_D(const Application);
207 return d->dispatcher->dispatchers();
208}
209
211{
212 Q_D(const Application);
213 return d->plugins;
214}
215
216QVariantMap Application::config() const noexcept
217{
218 Q_D(const Application);
219 return d->config;
220}
221
223{
224 QDir home = config(QStringLiteral("home")).toString();
225 return home.absoluteFilePath(path);
226}
227
229{
230 QDir home = config(QStringLiteral("home")).toString();
231 return home.absoluteFilePath(path.join(u'/'));
232}
233
234bool Cutelyst::Application::inited() const noexcept
235{
236 Q_D(const Application);
237 return d->init;
238}
239
241{
242 Q_D(const Application);
243 return d->engine;
244}
245
246void Application::setConfig(const QString &key, const QVariant &value)
247{
248 Q_D(Application);
249 d->config.insert(key, value);
250}
251
253{
254 Q_D(Application);
255
256 if (d->init) {
257 return true;
258 }
259 d->init = true;
260
261 d->useStats = CUTELYST_STATS().isDebugEnabled();
262 d->engine = engine;
263 d->config = engine->config(QLatin1String("Cutelyst"));
264
265 d->setupHome();
266
267 // Call the virtual application init
268 // to setup Controllers plugins stuff
269 if (init()) {
270 d->setupChildren(children());
271
272 bool zeroCore = engine->workerCore() == 0;
273
274 QVector<QStringList> tablePlugins;
275 const auto plugins = d->plugins;
276 for (Plugin *plugin : plugins) {
277 if (plugin->objectName().isEmpty()) {
278 plugin->setObjectName(QString::fromLatin1(plugin->metaObject()->className()));
279 }
280 tablePlugins.append({plugin->objectName()});
281 // Configure plugins
282 plugin->setup(this);
283 }
284
285 if (zeroCore && !tablePlugins.isEmpty()) {
286 qCDebug(CUTELYST_CORE)
287 << Utils::buildTable(tablePlugins, QStringList(), QLatin1String("Loaded plugins:"))
288 .constData();
289 }
290
291 if (zeroCore) {
292 QVector<QStringList> tableDataHandlers;
293 tableDataHandlers.append({QLatin1String("application/x-www-form-urlencoded")});
294 tableDataHandlers.append({QLatin1String("application/json")});
295 tableDataHandlers.append({QLatin1String("multipart/form-data")});
296 qCDebug(CUTELYST_CORE)
297 << Utils::buildTable(tableDataHandlers,
298 QStringList(),
299 QLatin1String("Loaded Request Data Handlers:"))
300 .constData();
301
302 qCDebug(CUTELYST_CORE) << "Loaded dispatcher"
303 << QString::fromLatin1(d->dispatcher->metaObject()->className());
304 qCDebug(CUTELYST_CORE)
305 << "Using engine" << QString::fromLatin1(d->engine->metaObject()->className());
306 }
307
308 QString home = d->config.value(QLatin1String("home")).toString();
309 if (home.isEmpty()) {
310 if (zeroCore) {
311 qCDebug(CUTELYST_CORE) << "Couldn't find home";
312 }
313 } else {
314 QFileInfo homeInfo(home);
315 if (homeInfo.isDir()) {
316 if (zeroCore) {
317 qCDebug(CUTELYST_CORE) << "Found home" << home;
318 }
319 } else {
320 if (zeroCore) {
321 qCDebug(CUTELYST_CORE) << "Home" << home << "doesn't exist";
322 }
323 }
324 }
325
327 QStringList controllerNames = d->controllersHash.keys();
328 controllerNames.sort();
329 for (const QString &controller : controllerNames) {
330 table.append({controller, QLatin1String("Controller")});
331 }
332
333 const auto views = d->views;
334 for (View *view : views) {
335 if (view->reverse().isEmpty()) {
336 const QString className = QString::fromLatin1(view->metaObject()->className()) +
337 QLatin1String("->execute");
338 view->setReverse(className);
339 }
340 table.append({view->reverse(), QLatin1String("View")});
341 }
342
343 if (zeroCore && !table.isEmpty()) {
344 qCDebug(CUTELYST_CORE)
345 << Utils::buildTable(table,
346 {QLatin1String("Class"), QLatin1String("Type")},
347 QLatin1String("Loaded components:"))
348 .constData();
349 }
350
351 const auto controllers = d->controllers;
352 for (Controller *controller : controllers) {
353 controller->d_ptr->init(this, d->dispatcher);
354 }
355
356 d->dispatcher->setupActions(d->controllers, d->dispatchers, d->engine->workerCore() == 0);
357
358 if (zeroCore) {
359 qCInfo(CUTELYST_CORE) << qPrintable(
360 QString::fromLatin1("%1 powered by Cutelyst %2, Qt %3.")
363 QLatin1String(qVersion())));
364 }
365
366 Q_EMIT preForked(this);
367
368 return true;
369 }
370
371 return false;
372}
373
375{
376 Q_D(Application);
377
378 Engine *engine = d->engine;
379
380 auto priv = new ContextPrivate(this, engine, d->dispatcher, d->plugins);
381 auto c = new Context(priv);
382
383 request->context = c;
384 priv->engineRequest = request;
385 priv->response = new Response(d->headers, request);
386 priv->request = new Request(request);
387
388 if (d->useStats) {
389 priv->stats = new Stats(request);
390 }
391
392 // Process request
393 bool skipMethod = false;
394 Q_EMIT beforePrepareAction(c, &skipMethod);
395
396 if (!skipMethod) {
397 static bool log = CUTELYST_REQUEST().isEnabled(QtDebugMsg);
398 if (log) {
399 d->logRequest(priv->request);
400 }
401
402 d->dispatcher->prepareAction(c);
403
405
406 d->dispatcher->dispatch(c);
407
408 if (request->status & EngineRequest::Async) {
409 return;
410 }
411
413 }
414
415 c->finalize();
416}
417
419{
420 Q_D(Application);
421
422 if (!postFork()) {
423 return false;
424 }
425
426 const auto controllers = d->controllers;
427 for (Controller *controller : controllers) {
428 if (!controller->postFork(this)) {
429 return false;
430 }
431 }
432
433 Q_EMIT postForked(this);
434
435 return true;
436}
437
438void Application::addTranslator(const QLocale &locale, QTranslator *translator)
439{
440 Q_D(Application);
441 Q_ASSERT_X(translator, "add translator to application", "invalid QTranslator object");
442 auto it = d->translators.find(locale);
443 if (it != d->translators.end()) {
444 it.value().prepend(translator);
445 } else {
446 d->translators.insert(locale, QVector<QTranslator *>(1, translator));
447 }
448}
449
450void Application::addTranslator(const QString &locale, QTranslator *translator)
451{
452 addTranslator(QLocale(locale), translator);
453}
454
455void Application::addTranslators(const QLocale &locale, const QVector<QTranslator *> &translators)
456{
457 Q_D(Application);
458 Q_ASSERT_X(!translators.empty(), "add translators to application", "empty translators vector");
459 auto transIt = d->translators.find(locale);
460 if (transIt != d->translators.end()) {
461 for (auto it = translators.crbegin(); it != translators.crend(); ++it) {
462 transIt.value().prepend(*it);
463 }
464 } else {
465 d->translators.insert(locale, translators);
466 }
467}
468
469static void replacePercentN(QString *result, int n)
470{
471 if (n >= 0) {
472 auto percentPos = 0;
473 auto len = 0;
474 while ((percentPos = result->indexOf(u'%', percentPos + len)) != -1) {
475 len = 1;
476 QString fmt;
477 if (result->at(percentPos + len) == u'L') {
478 ++len;
479 fmt = QStringLiteral("%L1");
480 } else {
481 fmt = QStringLiteral("%1");
482 }
483 if (result->at(percentPos + len) == u'n') {
484 fmt = fmt.arg(n);
485 ++len;
486 result->replace(percentPos, len, fmt);
487 len = fmt.length();
488 }
489 }
490 }
491}
492
494 const char *context,
495 const char *sourceText,
496 const char *disambiguation,
497 int n) const
498{
499 QString result;
500
501 if (!sourceText) {
502 return result;
503 }
504
505 Q_D(const Application);
506
507 const QVector<QTranslator *> translators = d->translators.value(locale);
508 if (translators.empty()) {
509 result = QString::fromUtf8(sourceText);
510 replacePercentN(&result, n);
511 return result;
512 }
513
514 for (QTranslator *translator : translators) {
515 result = translator->translate(context, sourceText, disambiguation, n);
516 if (!result.isEmpty()) {
517 break;
518 }
519 }
520
521 if (result.isEmpty()) {
522 result = QString::fromUtf8(sourceText);
523 }
524
525 replacePercentN(&result, n);
526 return result;
527}
528
530 const QString &directory,
531 const QString &prefix,
532 const QString &suffix)
533{
534 loadTranslationsFromDir(filename, directory, prefix, suffix);
535}
536
538 const QString &directory,
539 const QString &prefix,
540 const QString &suffix)
541{
542 QVector<QLocale> locales;
543
544 if (Q_LIKELY(!filename.isEmpty())) {
545 const QString _dir = directory.isEmpty() ? QStringLiteral(I18NDIR) : directory;
546 const QDir i18nDir(_dir);
547 if (Q_LIKELY(i18nDir.exists())) {
548 const QString _prefix = prefix.isEmpty() ? QStringLiteral(".") : prefix;
549 const QString _suffix = suffix.isEmpty() ? QStringLiteral(".qm") : suffix;
550 const QStringList namesFilter = QStringList({filename + _prefix + u'*' + _suffix});
551
552 const QFileInfoList tsFiles = i18nDir.entryInfoList(namesFilter, QDir::Files);
553 if (Q_LIKELY(!tsFiles.empty())) {
554 locales.reserve(tsFiles.size());
555 for (const QFileInfo &ts : tsFiles) {
556 const QString fn = ts.fileName();
557 const int prefIdx = fn.indexOf(_prefix);
558 const QString locString =
559 fn.mid(prefIdx + _prefix.length(),
560 fn.length() - prefIdx - _suffix.length() - _prefix.length());
561 QLocale loc(locString);
562 if (Q_LIKELY(loc.language() != QLocale::C)) {
563 auto trans = new QTranslator(this);
564 if (Q_LIKELY(trans->load(loc, filename, _prefix, _dir))) {
565 addTranslator(loc, trans);
566 locales.append(loc);
567 qCDebug(CUTELYST_CORE) << "Loaded translations for" << loc << "from"
568 << ts.absoluteFilePath();
569 } else {
570 delete trans;
571 qCWarning(CUTELYST_CORE) << "Can not load translations for" << loc
572 << "from" << ts.absoluteFilePath();
573 }
574 } else {
575 qCWarning(CUTELYST_CORE)
576 << "Can not load translations for invalid locale string" << locString;
577 }
578 }
579 locales.squeeze();
580 } else {
581 qCWarning(CUTELYST_CORE)
582 << "Can not find translation files for" << filename << "in directory" << _dir;
583 }
584 } else {
585 qCWarning(CUTELYST_CORE)
586 << "Can not load translations from not existing directory:" << _dir;
587 }
588 } else {
589 qCWarning(CUTELYST_CORE) << "Can not load translations for empty file name.";
590 }
591
592 return locales;
593}
594
596 const QString &filename)
597{
598 QVector<QLocale> locales;
599
600 if (Q_LIKELY(!directory.isEmpty() && !filename.isEmpty())) {
601 const QDir dir(directory);
602 if (Q_LIKELY(dir.exists())) {
603 const auto dirs = dir.entryList(QDir::AllDirs);
604 if (Q_LIKELY(!dirs.empty())) {
605 locales.reserve(dirs.size());
606 for (const QString &subDir : dirs) {
607 const QString relFn = subDir + u'/' + filename;
608 if (dir.exists(relFn)) {
609 const QLocale l(subDir);
610 if (Q_LIKELY(l.language() != QLocale::C)) {
611 auto trans = new QTranslator(this);
612 const QFileInfo fi(dir, relFn);
613 if (Q_LIKELY(trans->load(
614 l, fi.baseName(), QString(), fi.absolutePath(), fi.suffix()))) {
615 addTranslator(l, trans);
616 locales.append(l);
617 qCDebug(CUTELYST_CORE) << "Loaded translations for" << l << "from"
618 << fi.absoluteFilePath();
619 } else {
620 delete trans;
621 qCWarning(CUTELYST_CORE) << "Can not load translations for" << l
622 << "from" << fi.absoluteFilePath();
623 }
624 } else {
625 qCWarning(CUTELYST_CORE)
626 << "Can not load translations for invalid locale string:" << subDir;
627 }
628 }
629 }
630 locales.squeeze();
631 } else {
632 qCWarning(CUTELYST_CORE) << "Can not find locale dirs under" << directory;
633 }
634 } else {
635 qCWarning(CUTELYST_CORE)
636 << "Can not load translations from not existing directory:" << directory;
637 }
638 } else {
639 qCWarning(CUTELYST_CORE)
640 << "Can not load translations for empty file name or directory name";
641 }
642
643 return locales;
644}
645
646void Cutelyst::ApplicationPrivate::setupHome()
647{
648 // Hook the current directory in config if "home" is not set
649 if (!config.contains(QLatin1String("home"))) {
650 config.insert(QStringLiteral("home"), QDir::currentPath());
651 }
652
653 if (!config.contains(QLatin1String("root"))) {
654 QDir home = config.value(QLatin1String("home")).toString();
655 config.insert(QStringLiteral("root"), home.absoluteFilePath(QLatin1String("root")));
656 }
657}
658
659void ApplicationPrivate::setupChildren(const QObjectList &children)
660{
661 Q_Q(Application);
662 for (QObject *child : children) {
663 auto controller = qobject_cast<Controller *>(child);
664 if (controller) {
665 q->registerController(controller);
666 continue;
667 }
668
669 auto plugin = qobject_cast<Plugin *>(child);
670 if (plugin) {
671 q->registerPlugin(plugin);
672 continue;
673 }
674
675 auto view = qobject_cast<View *>(child);
676 if (view) {
677 q->registerView(view);
678 continue;
679 }
680
681 auto dispatchType = qobject_cast<DispatchType *>(child);
682 if (dispatchType) {
683 q->registerDispatcher(dispatchType);
684 continue;
685 }
686 }
687}
688
689void Cutelyst::ApplicationPrivate::logRequest(Request *req)
690{
691 QString path = req->path();
692 if (path.isEmpty()) {
693 path = QStringLiteral("/");
694 }
695 qCDebug(CUTELYST_REQUEST) << req->method() << "request for" << path << "from"
696 << req->addressString();
697
698 ParamsMultiMap params = req->queryParameters();
699 if (!params.isEmpty()) {
700 logRequestParameters(params, QLatin1String("Query Parameters are:"));
701 }
702
703 params = req->bodyParameters();
704 if (!params.isEmpty()) {
705 logRequestParameters(params, QLatin1String("Body Parameters are:"));
706 }
707
708 const auto uploads = req->uploads();
709 if (!uploads.isEmpty()) {
710 logRequestUploads(uploads);
711 }
712}
713
714void Cutelyst::ApplicationPrivate::logRequestParameters(const ParamsMultiMap &params,
715 const QString &title)
716{
717 QVector<QStringList> table;
718 auto it = params.constBegin();
719 while (it != params.constEnd()) {
720 table.append({it.key(), it.value()});
721 ++it;
722 }
723 qCDebug(CUTELYST_REQUEST) << Utils::buildTable(table,
724 {
725 QLatin1String("Parameter"),
726 QLatin1String("Value"),
727 },
728 title)
729 .constData();
730}
731
732void Cutelyst::ApplicationPrivate::logRequestUploads(const QVector<Cutelyst::Upload *> &uploads)
733{
734 QVector<QStringList> table;
735 for (Upload *upload : uploads) {
736 table.append({upload->name(),
737 upload->filename(),
738 upload->contentType(),
739 QString::number(upload->size())});
740 }
741 qCDebug(CUTELYST_REQUEST) << Utils::buildTable(table,
742 {
743 QLatin1String("Parameter"),
744 QLatin1String("Filename"),
745 QLatin1String("Type"),
746 QLatin1String("Size"),
747 },
748 QLatin1String("File Uploads are:"))
749 .constData();
750}
751
752Component *ApplicationPrivate::createComponentPlugin(const QString &name,
753 QObject *parent,
754 const QString &directory)
755{
756 Component *component = nullptr;
757
758 QDir pluginsDir(directory);
759 QPluginLoader loader;
760 ComponentFactory *factory = nullptr;
761 const auto plugins = pluginsDir.entryList(QDir::Files);
762 for (const QString &fileName : plugins) {
763 loader.setFileName(pluginsDir.absoluteFilePath(fileName));
764 const QJsonObject json = loader.metaData()[QLatin1String("MetaData")].toObject();
765 if (json[QLatin1String("name")].toString() == name) {
766 QObject *plugin = loader.instance();
767 if (plugin) {
768 factory = qobject_cast<ComponentFactory *>(plugin);
769 if (!factory) {
770 qCCritical(CUTELYST_CORE)
771 << "Could not create a factory for" << loader.fileName();
772 } else {
773 component = factory->createComponent(parent);
774 }
775 break;
776 } else {
777 qCCritical(CUTELYST_CORE)
778 << "Could not load plugin" << loader.fileName() << loader.errorString();
779 }
780 }
781 }
782
783 if (factory) {
784 factories.insert(name, factory);
785 }
786
787 return component;
788}
789
790#include "moc_application.cpp"
The Cutelyst Application.
Definition application.h:43
Engine * engine() const noexcept
QVector< Controller * > controllers() const noexcept
void afterDispatch(Cutelyst::Context *c)
bool setup(Engine *engine)
Called by the Engine to setup the internal data.
void handleRequest(Cutelyst::EngineRequest *request)
Called by the Engine to handle a new Request object.
void beforePrepareAction(Cutelyst::Context *c, bool *skipMethod)
Application(QObject *parent=nullptr)
void setConfig(const QString &key, const QVariant &value)
QVariantMap config() const noexcept
Dispatcher * dispatcher() const noexcept
bool registerPlugin(Plugin *plugin)
bool registerController(Controller *controller)
bool registerDispatcher(DispatchType *dispatcher)
QString pathTo(const QString &path) const
Headers & defaultHeaders() noexcept
QString translate(const QLocale &locale, const char *context, const char *sourceText, const char *disambiguation=nullptr, int n=-1) const
bool inited() const noexcept
void beforeDispatch(Cutelyst::Context *c)
void preForked(Cutelyst::Application *app)
QVector< Plugin * > plugins() const noexcept
bool enginePostFork()
Called by the Engine once post fork happened.
void addTranslator(const QLocale &locale, QTranslator *translator)
bool registerView(View *view)
void addTranslators(const QLocale &locale, const QVector< QTranslator * > &translators)
QVector< DispatchType * > dispatchers() const noexcept
static const char * cutelystVersion() noexcept
QVector< QLocale > loadTranslationsFromDirs(const QString &directory, const QString &filename)
T plugin()
Returns the registered plugin that casts to the template type T.
Component * createComponentPlugin(const QString &name, QObject *parent=nullptr)
QVariant config(const QString &key, const QVariant &defaultValue={}) const
QVector< QLocale > loadTranslationsFromDir(const QString &filename, const QString &directory=QString(), const QString &prefix=QStringLiteral("."), const QString &suffix=QStringLiteral(".qm"))
View * view(const QString &name) const
virtual bool init()
void loadTranslations(const QString &filename, const QString &directory=QString(), const QString &prefix=QString(), const QString &suffix=QString())
void postForked(Cutelyst::Application *app)
virtual bool postFork()
virtual Component * createComponent(QObject *parent=nullptr)=0
The Cutelyst Component base class.
Definition component.h:26
Cutelyst Controller base class
Definition controller.h:88
The Cutelyst Dispatcher.
Definition dispatcher.h:28
Context * context
The Cutelyst::Context of this request.
Status status
Connection status.
The Cutelyst Engine.
Definition engine.h:21
QString addressString() const
Definition request.cpp:39
QVector< Upload * > uploads() const
Definition request.cpp:377
ParamsMultiMap bodyParameters() const
Definition request.cpp:216
ParamsMultiMap queryParameters() const
Definition request.cpp:252
Cutelyst Upload handles file upload request
Definition upload.h:23
Cutelyst View abstract view component
Definition view.h:22
The Cutelyst namespace holds all public Cutelyst API.
Definition Mainpage.dox:8
QMultiMap< QString, QString > ParamsMultiMap
qint64 applicationPid()
QString absoluteFilePath(const QString &fileName) const const
QString currentPath()
QFileInfoList entryInfoList(QDir::Filters filters, QDir::SortFlags sort) const const
QStringList entryList(QDir::Filters filters, QDir::SortFlags sort) const const
bool exists() const const
QString absoluteFilePath() const const
QString absolutePath() const const
QString baseName() const const
bool isDir() const const
QString suffix() const const
void append(QList< T > &&value)
QList< T >::const_reverse_iterator crbegin() const const
QList< T >::const_reverse_iterator crend() const const
bool empty() const const
bool isEmpty() const const
void reserve(qsizetype size)
void squeeze()
T value(qsizetype i) const const
QLocale::Language language() const const
const char * className() const const
QMultiMap< Key, T >::const_iterator constBegin() const const
QMultiMap< Key, T >::const_iterator constEnd() const const
bool isEmpty() const const
QObject(QObject *parent)
Q_EMITQ_EMIT
const QObjectList & children() const const
virtual const QMetaObject * metaObject() const const
QObject * parent() const const
QString arg(Args &&... args) const const
const QChar at(qsizetype position) const const
QString fromLatin1(QByteArrayView str)
QString fromLocal8Bit(QByteArrayView str)
QString fromUtf8(QByteArrayView str)
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
qsizetype length() const const
QString mid(qsizetype position, qsizetype n) &&
QString number(double n, char format, int precision)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
QString join(QChar separator) const const
void sort(Qt::CaseSensitivity cs)