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