cutelyst 3.9.1
A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.
langselect.cpp
1/*
2 * SPDX-FileCopyrightText: (C) 2018-2022 Matthias Fehring <mf@huessenbergnetz.de>
3 * SPDX-License-Identifier: BSD-3-Clause
4 */
5
6#include "langselect_p.h"
7
8#include <Cutelyst/Application>
9#include <Cutelyst/Context>
10#include <Cutelyst/Plugins/Session/Session>
11#include <Cutelyst/Response>
12#include <map>
13#include <utility>
14
15#include <QDir>
16#include <QFileInfo>
17#include <QLoggingCategory>
18#include <QNetworkCookie>
19#include <QUrl>
20#include <QUrlQuery>
21
22Q_LOGGING_CATEGORY(C_LANGSELECT, "cutelyst.plugin.langselect", QtWarningMsg)
23
24using namespace Cutelyst;
25
26static thread_local LangSelect *lsp = nullptr;
27
28#define SELECTION_TRIED QStringLiteral("_c_langselect_tried")
29
31 : Plugin(parent)
32 , d_ptr(new LangSelectPrivate)
33{
34 Q_D(LangSelect);
35 d->source = source;
36 d->autoDetect = true;
37}
38
40 : Plugin(parent)
41 , d_ptr(new LangSelectPrivate)
42{
43 Q_D(LangSelect);
44 d->source = AcceptHeader;
45 d->autoDetect = false;
46}
47
49{
50 delete d_ptr;
51}
52
54{
55 Q_D(LangSelect);
56 if (d->fallbackLocale.language() == QLocale::C) {
57 qCCritical(C_LANGSELECT, "We need a valid fallback locale.");
58 return false;
59 }
60 if (d->autoDetect) {
61 if (d->source < Fallback) {
62 if (d->source == URLQuery && d->queryKey.isEmpty()) {
63 qCCritical(C_LANGSELECT, "Can not use url query as source with empty key name.");
64 return false;
65 } else if (d->source == Session && d->sessionKey.isEmpty()) {
66 qCCritical(C_LANGSELECT, "Can not use session as source with empty key name.");
67 return false;
68 } else if (d->source == Cookie && d->cookieName.isEmpty()) {
69 qCCritical(C_LANGSELECT, "Can not use cookie as source with empty cookie name.");
70 return false;
71 }
72 } else {
73 qCCritical(C_LANGSELECT, "Invalid source.");
74 return false;
75 }
76 connect(app, &Application::beforePrepareAction, this, [d](Context *c, bool *skipMethod) {
77 d->beforePrepareAction(c, skipMethod);
78 });
79 }
80 if (!d->locales.contains(d->fallbackLocale)) {
81 d->locales.append(d->fallbackLocale);
82 }
83 connect(app, &Application::postForked, this, &LangSelectPrivate::_q_postFork);
84
85 qCDebug(C_LANGSELECT) << "Initialized LangSelect plugin with the following settings:";
86 qCDebug(C_LANGSELECT) << "Supported locales:" << d->locales;
87 qCDebug(C_LANGSELECT) << "Fallback locale:" << d->fallbackLocale;
88 qCDebug(C_LANGSELECT) << "Auto detection source:" << d->source;
89 qCDebug(C_LANGSELECT) << "Detect from header:" << d->detectFromHeader;
90
91 return true;
92}
93
95{
96 Q_D(LangSelect);
97 d->locales.clear();
98 d->locales.reserve(locales.size());
99 for (const QLocale &l : locales) {
100 if (Q_LIKELY(l.language() != QLocale::C)) {
101 d->locales.push_back(l);
102 } else {
103 qCWarning(C_LANGSELECT)
104 << "Can not add invalid locale" << l << "to the list of supported locales.";
105 }
106 }
107}
108
110{
111 Q_D(LangSelect);
112 d->locales.clear();
113 d->locales.reserve(locales.size());
114 for (const QString &l : locales) {
115 QLocale locale(l);
116 if (Q_LIKELY(locale.language() != QLocale::C)) {
117 d->locales.push_back(locale);
118 } else {
119 qCWarning(C_LANGSELECT,
120 "Can not add invalid locale \"%s\" to the list of supported locales.",
121 qUtf8Printable(l));
122 }
123 }
124}
125
127{
128 if (Q_LIKELY(locale.language() != QLocale::C)) {
129 Q_D(LangSelect);
130 d->locales.push_back(locale);
131 } else {
132 qCWarning(C_LANGSELECT) << "Can not add invalid locale" << locale
133 << "to the list of supported locales.";
134 }
135}
136
138{
139 QLocale l(locale);
140 if (Q_LIKELY(l.language() != QLocale::C)) {
141 Q_D(LangSelect);
142 d->locales.push_back(l);
143 } else {
144 qCWarning(C_LANGSELECT,
145 "Can not add invalid locale \"%s\" to the list of supported locales.",
146 qUtf8Printable(locale));
147 }
148}
149
151 const QString &name,
152 const QString &prefix,
153 const QString &suffix)
154{
155 Q_D(LangSelect);
156 d->locales.clear();
157 if (Q_LIKELY(!path.isEmpty() && !name.isEmpty())) {
158 const QDir dir(path);
159 if (Q_LIKELY(dir.exists())) {
160 const auto _pref = prefix.isEmpty() ? QStringLiteral(".") : prefix;
161 const auto _suff = suffix.isEmpty() ? QStringLiteral(".qm") : suffix;
162 const QString filter = name + _pref + u'*' + _suff;
163 const auto files = dir.entryInfoList({name}, QDir::Files);
164 if (Q_LIKELY(!files.empty())) {
165 d->locales.reserve(files.size());
166 bool shrinkToFit = false;
167 for (const QFileInfo &fi : files) {
168 const auto fn = fi.fileName();
169 const auto prefIdx = fn.indexOf(_pref);
170 const auto locPart =
171 fn.mid(prefIdx + _pref.length(),
172 fn.length() - prefIdx - _suff.length() - _pref.length());
173 QLocale l(locPart);
174 if (Q_LIKELY(l.language() != QLocale::C)) {
175 d->locales.push_back(l);
176 qCDebug(C_LANGSELECT,
177 "Added locale \"%s\" to the list of supported locales.",
178 qUtf8Printable(locPart));
179 } else {
180 shrinkToFit = true;
181 qCWarning(
182 C_LANGSELECT,
183 "Can not add invalid locale \"%s\" to the list of supported locales.",
184 qUtf8Printable(locPart));
185 }
186 }
187 if (shrinkToFit) {
188 d->locales.squeeze();
189 }
190 } else {
191 qCWarning(C_LANGSELECT,
192 "Can not find translation files for \"%s\" in \"%s\".",
193 qUtf8Printable(filter),
194 qUtf8Printable(path));
195 }
196 } else {
197 qCWarning(C_LANGSELECT,
198 "Can not set locales from not existing directory \"%s\".",
199 qUtf8Printable(path));
200 }
201 } else {
202 qCWarning(C_LANGSELECT, "Can not set locales from dir with empty path or name.");
203 }
204}
205
206void LangSelect::setLocalesFromDirs(const QString &path, const QString &name)
207{
208 Q_D(LangSelect);
209 d->locales.clear();
210 if (Q_LIKELY(!path.isEmpty() && !name.isEmpty())) {
211 const QDir dir(path);
212 if (Q_LIKELY(dir.exists())) {
213 const auto dirs = dir.entryList(QDir::AllDirs);
214 if (Q_LIKELY(!dirs.empty())) {
215 d->locales.reserve(dirs.size());
216 bool shrinkToFit = false;
217 for (const QString &subDir : dirs) {
218 const QString relFn = subDir + u'/' + name;
219 if (dir.exists(relFn)) {
220 QLocale l(subDir);
221 if (Q_LIKELY(l.language() != QLocale::C)) {
222 d->locales.push_back(l);
223 qCDebug(C_LANGSELECT,
224 "Added locale \"%s\" to the list of supported locales.",
225 qUtf8Printable(subDir));
226 } else {
227 shrinkToFit = true;
228 qCWarning(C_LANGSELECT,
229 "Can not add invalid locale \"%s\" to the list of supported "
230 "locales.",
231 qUtf8Printable(subDir));
232 }
233 } else {
234 shrinkToFit = true;
235 }
236 }
237 if (shrinkToFit) {
238 d->locales.squeeze();
239 }
240 }
241 } else {
242 qCWarning(C_LANGSELECT,
243 "Can not set locales from not existing directory \"%s\".",
244 qUtf8Printable(path));
245 }
246 } else {
247 qCWarning(C_LANGSELECT, "Can not set locales from dirs with empty path or names.");
248 }
249}
250
252{
253 Q_D(const LangSelect);
254 return d->locales;
255}
256
258{
259 Q_D(LangSelect);
260 d->queryKey = key;
261}
262
264{
265 Q_D(LangSelect);
266 d->sessionKey = key;
267}
268
270{
271 Q_D(LangSelect);
272 d->cookieName = name;
273}
274
276{
277 Q_D(LangSelect);
278 d->subDomainMap.clear();
279 d->locales.clear();
280 d->locales.reserve(map.size());
281 auto i = map.constBegin();
282 while (i != map.constEnd()) {
283 if (i.value().language() != QLocale::C) {
284 d->subDomainMap.insert(i.key(), i.value());
285 d->locales.append(i.value());
286 } else {
287 qCWarning(C_LANGSELECT) << "Can not add invalid locale" << i.value() << "for subdomain"
288 << i.key() << "to the subdomain map.";
289 }
290 ++i;
291 }
292 d->locales.squeeze();
293}
294
296{
297 Q_D(LangSelect);
298 d->domainMap.clear();
299 d->locales.clear();
300 d->locales.reserve(map.size());
301 auto i = map.constBegin();
302 while (i != map.constEnd()) {
303 if (Q_LIKELY(i.value().language() != QLocale::C)) {
304 d->domainMap.insert(i.key(), i.value());
305 d->locales.append(i.value());
306 } else {
307 qCWarning(C_LANGSELECT) << "Can not add invalid locale" << i.value() << "for domain"
308 << i.key() << "to the domain map.";
309 }
310 ++i;
311 }
312 d->locales.squeeze();
313}
314
316{
317 Q_D(LangSelect);
318 d->fallbackLocale = fallback;
319}
320
322{
323 Q_D(LangSelect);
324 d->detectFromHeader = enabled;
325}
326
328{
329 Q_D(LangSelect);
330 if (Q_LIKELY(!key.isEmpty())) {
331 d->langStashKey = key;
332 } else {
333 qCWarning(C_LANGSELECT) << "Can not set an empty key name for the language code stash key. "
334 "Using current key name"
335 << d->langStashKey;
336 }
337}
338
340{
341 Q_D(LangSelect);
342 if (Q_LIKELY(!key.isEmpty())) {
343 d->dirStashKey = key;
344 } else {
345 qCWarning(C_LANGSELECT) << "Can not set an empty key name for the language direction stash "
346 "key. Using current key name"
347 << d->dirStashKey;
348 }
349}
350
352{
353 if (!lsp) {
354 qCCritical(C_LANGSELECT) << "LangSelect plugin not registered";
355 return QVector<QLocale>();
356 }
357
358 return lsp->supportedLocales();
359}
360
362{
363 if (!lsp) {
364 qCCritical(C_LANGSELECT) << "LangSelect plugin not registered";
365 return true;
366 }
367
368 const auto d = lsp->d_ptr;
369 const auto _key = !key.isEmpty() ? key : d->queryKey;
370 if (!d->getFromQuery(c, _key)) {
371 if (!d->getFromHeader(c)) {
372 d->setFallback(c);
373 }
374 d->setToQuery(c, _key);
375 c->detach();
376 return false;
377 }
378 d->setContentLanguage(c);
379
380 return true;
381}
382
384{
385 bool foundInSession = false;
386
387 if (!lsp) {
388 qCCritical(C_LANGSELECT) << "LangSelect plugin not registered";
389 return foundInSession;
390 }
391
392 const auto d = lsp->d_ptr;
393 const auto _key = !key.isEmpty() ? key : d->sessionKey;
394 foundInSession = d->getFromSession(c, _key);
395 if (!foundInSession) {
396 if (!d->getFromHeader(c)) {
397 d->setFallback(c);
398 }
399 d->setToSession(c, _key);
400 }
401 d->setContentLanguage(c);
402
403 return foundInSession;
404}
405
407{
408 bool foundInCookie = false;
409
410 if (!lsp) {
411 qCCritical(C_LANGSELECT) << "LangSelect plugin not registered";
412 return foundInCookie;
413 }
414
415 const auto d = lsp->d_ptr;
416 const auto _name = !name.isEmpty() ? name : d->cookieName;
417 foundInCookie = d->getFromCookie(c, _name);
418 if (!foundInCookie) {
419 if (!d->getFromHeader(c)) {
420 d->setFallback(c);
421 }
422 d->setToCookie(c, _name);
423 }
424 d->setContentLanguage(c);
425
426 return foundInCookie;
427}
428
430{
431 bool foundInSubDomain = false;
432
433 if (!lsp) {
434 qCCritical(C_LANGSELECT) << "LangSelect plugin not registered";
435 return foundInSubDomain;
436 }
437
438 const auto d = lsp->d_ptr;
439 const auto _map = !subDomainMap.empty() ? subDomainMap : d->subDomainMap;
440 foundInSubDomain = d->getFromSubdomain(c, _map);
441 if (!foundInSubDomain) {
442 if (!d->getFromHeader(c)) {
443 d->setFallback(c);
444 }
445 }
446
447 d->setContentLanguage(c);
448
449 return foundInSubDomain;
450}
451
453{
454 bool foundInDomain = false;
455
456 if (!lsp) {
457 qCCritical(C_LANGSELECT) << "LangSelect plugin not registered";
458 return foundInDomain;
459 }
460
461 const auto d = lsp->d_ptr;
462 const auto _map = !domainMap.empty() ? domainMap : d->domainMap;
463 foundInDomain = d->getFromDomain(c, _map);
464 if (!foundInDomain) {
465 if (!d->getFromHeader(c)) {
466 d->setFallback(c);
467 }
468 }
469
470 d->setContentLanguage(c);
471
472 return foundInDomain;
473}
474
475bool LangSelect::fromPath(Context *c, const QString &locale)
476{
477 if (!lsp) {
478 qCCritical(C_LANGSELECT) << "LangSelect plugin not registered";
479 return true;
480 }
481
482 const auto d = lsp->d_ptr;
483 const QLocale l(locale);
484 if (l.language() != QLocale::C && d->locales.contains(l)) {
485 qCDebug(C_LANGSELECT) << "Found valid locale" << l << "in path";
486 c->setLocale(l);
487 d->setContentLanguage(c);
488 return true;
489 } else {
490 if (!d->getFromHeader(c)) {
491 d->setFallback(c);
492 }
493 auto uri = c->req()->uri();
494 auto pathParts = uri.path().split(u'/');
495 const auto localeIdx = pathParts.indexOf(locale);
496 pathParts[localeIdx] = c->locale().bcp47Name().toLower();
497 uri.setPath(pathParts.join(u'/'));
498 qCDebug(C_LANGSELECT) << "Storing selected locale by redirecting to" << uri;
499 c->res()->redirect(uri, 307);
500 c->detach();
501 return false;
502 }
503}
504
505bool LangSelectPrivate::detectLocale(Context *c, LangSelect::Source _source, bool *skipMethod) const
506{
507 bool redirect = false;
508
510
511 if (_source == LangSelect::Session) {
512 if (getFromSession(c, sessionKey)) {
513 foundIn = _source;
514 }
515 } else if (_source == LangSelect::Cookie) {
516 if (getFromCookie(c, cookieName)) {
517 foundIn = _source;
518 }
519 } else if (_source == LangSelect::URLQuery) {
520 if (getFromQuery(c, queryKey)) {
521 foundIn = _source;
522 }
523 } else if (_source == LangSelect::SubDomain) {
524 if (getFromSubdomain(c, subDomainMap)) {
525 foundIn = _source;
526 }
527 } else if (_source == LangSelect::Domain) {
528 if (getFromDomain(c, domainMap)) {
529 foundIn = _source;
530 }
531 }
532
533 // could not find supported locale in specified source
534 // falling back to Accept-Language header
535 if (foundIn == LangSelect::Fallback && getFromHeader(c)) {
536 foundIn = LangSelect::AcceptHeader;
537 }
538
539 if (foundIn == LangSelect::Fallback) {
540 setFallback(c);
541 }
542
543 if (foundIn != _source) {
544 if (_source == LangSelect::Session) {
545 setToSession(c, sessionKey);
546 } else if (_source == LangSelect::Cookie) {
547 setToCookie(c, cookieName);
548 } else if (_source == LangSelect::URLQuery) {
549 setToQuery(c, queryKey);
550 redirect = true;
551 if (skipMethod) {
552 *skipMethod = true;
553 }
554 }
555 }
556
557 if (!redirect) {
558 setContentLanguage(c);
559 }
560
561 return redirect;
562}
563
564bool LangSelectPrivate::getFromQuery(Context *c, const QString &key) const
565{
566 const QLocale l(c->req()->queryParam(key));
567 if (l.language() != QLocale::C && locales.contains(l)) {
568 qCDebug(C_LANGSELECT) << "Found valid locale" << l << "in url query key" << key;
569 c->setLocale(l);
570 return true;
571 } else {
572 qCDebug(C_LANGSELECT) << "Can not find supported locale in url query key" << key;
573 return false;
574 }
575}
576
577bool LangSelectPrivate::getFromCookie(Context *c, const QString &cookie) const
578{
579 const QLocale l(c->req()->cookie(cookie));
580 if (l.language() != QLocale::C && locales.contains(l)) {
581 qCDebug(C_LANGSELECT) << "Found valid locale" << l << "in cookie name" << cookie;
582 c->setLocale(l);
583 return true;
584 } else {
585 qCDebug(C_LANGSELECT) << "Can no find supported locale in cookie value with name" << cookie;
586 return false;
587 }
588}
589
590bool LangSelectPrivate::getFromSession(Context *c, const QString &key) const
591{
592 const QLocale l = Cutelyst::Session::value(c, key).toLocale();
593 if (l.language() != QLocale::C && locales.contains(l)) {
594 qCDebug(C_LANGSELECT) << "Found valid locale" << l << "in session key" << key;
595 c->setLocale(l);
596 return true;
597 } else {
598 qCDebug(C_LANGSELECT) << "Can not find supported locale in session value with key" << key;
599 return false;
600 }
601}
602
603bool LangSelectPrivate::getFromSubdomain(Context *c, const QMap<QString, QLocale> &map) const
604{
605 const auto domain = c->req()->uri().host();
606 auto i = map.constBegin();
607 while (i != map.constEnd()) {
608 if (domain.startsWith(i.key())) {
609 qCDebug(C_LANGSELECT) << "Found valid locale" << i.value()
610 << "in subdomain map for domain" << domain;
611 c->setLocale(i.value());
612 return true;
613 }
614 ++i;
615 }
616
617 const auto domainParts = domain.split(u'.', Qt::SkipEmptyParts);
618 if (domainParts.size() > 2) {
619 const QLocale l(domainParts.at(0));
620 if (l.language() != QLocale::C && locales.contains(l)) {
621 qCDebug(C_LANGSELECT) << "Found supported locale" << l << "in subdomain of domain"
622 << domain;
623 c->setLocale(l);
624 return true;
625 }
626 }
627 qCDebug(C_LANGSELECT) << "Can not find supported locale for subdomain" << domain;
628 return false;
629}
630
631bool LangSelectPrivate::getFromDomain(Context *c, const QMap<QString, QLocale> &map) const
632{
633 const auto domain = c->req()->uri().host();
634 auto i = map.constBegin();
635 while (i != map.constEnd()) {
636 if (domain.endsWith(i.key())) {
637 qCDebug(C_LANGSELECT) << "Found valid locale" << i.value() << "in domain map for domain"
638 << domain;
639 c->setLocale(i.value());
640 return true;
641 }
642 ++i;
643 }
644
645 const auto domainParts = domain.split(u'.', Qt::SkipEmptyParts);
646 if (domainParts.size() > 1) {
647 const QLocale l(domainParts.at(domainParts.size() - 1));
648 if (l.language() != QLocale::C && locales.contains(l)) {
649 qCDebug(C_LANGSELECT) << "Found supported locale" << l << "in domain" << domain;
650 c->setLocale(l);
651 return true;
652 }
653 }
654 qCDebug(C_LANGSELECT) << "Can not find supported locale for domain" << domain;
655 return false;
656}
657
658bool LangSelectPrivate::getFromHeader(Context *c, const QString &name) const
659{
660 if (detectFromHeader) {
661 const auto accpetedLangs = c->req()->header(name).split(u',', Qt::SkipEmptyParts);
662 if (Q_LIKELY(!accpetedLangs.empty())) {
663 std::map<float, QLocale> langMap;
664 for (const QString &al : accpetedLangs) {
665 const auto idx = al.indexOf(u';');
666 float priority = 1.0f;
667 QString langPart;
668 bool ok = true;
669 if (idx > -1) {
670 langPart = al.left(idx);
671 const auto ref = QStringView(al).mid(idx + 1);
672 priority = ref.mid(ref.indexOf(u'=') + 1).toFloat(&ok);
673 } else {
674 langPart = al;
675 }
676 QLocale locale(langPart);
677 if (ok && locale.language() != QLocale::C) {
678 const auto search = langMap.find(priority);
679 if (search == langMap.cend()) {
680 langMap.insert({priority, locale});
681 }
682 }
683 }
684 if (!langMap.empty()) {
685 auto i = langMap.crbegin();
686 while (i != langMap.crend()) {
687 if (locales.contains(i->second)) {
688 c->setLocale(i->second);
689 qCDebug(C_LANGSELECT)
690 << "Selected locale" << c->locale() << "from" << name << "header";
691 return true;
692 }
693 ++i;
694 }
695 // if there is no exact match, lets try to find a locale
696 // where at least the language matches
697 i = langMap.crbegin();
698 const auto constLocales = locales;
699 while (i != langMap.crend()) {
700 for (const QLocale &l : constLocales) {
701 if (l.language() == i->second.language()) {
702 c->setLocale(l);
703 qCDebug(C_LANGSELECT)
704 << "Selected locale" << c->locale() << "from" << name << "header";
705 return true;
706 }
707 }
708 ++i;
709 }
710 }
711 }
712 }
713
714 return false;
715}
716
717void LangSelectPrivate::setToQuery(Context *c, const QString &key) const
718{
719 auto uri = c->req()->uri();
720 QUrlQuery query(uri);
721 if (query.hasQueryItem(key)) {
722 query.removeQueryItem(key);
723 }
724 query.addQueryItem(key, c->locale().bcp47Name().toLower());
725 uri.setQuery(query);
726 qCDebug(C_LANGSELECT) << "Storing selected locale in URL query by redirecting to" << uri;
727 c->res()->redirect(uri, 307);
728}
729
730void LangSelectPrivate::setToCookie(Context *c, const QString &name) const
731{
732 qCDebug(C_LANGSELECT) << "Storing selected locale in cookie with name" << name;
733 QNetworkCookie cookie(name.toLatin1(), c->locale().bcp47Name().toLatin1());
734#if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0)
735 cookie.setSameSitePolicy(QNetworkCookie::SameSite::Lax);
736#endif
737 c->res()->setCookie(cookie);
738}
739
740void LangSelectPrivate::setToSession(Context *c, const QString &key) const
741{
742 qCDebug(C_LANGSELECT) << "Storing selected locale in session key" << key;
743 Session::setValue(c, key, c->locale());
744}
745
746void LangSelectPrivate::setFallback(Context *c) const
747{
748 qCDebug(C_LANGSELECT) << "Can not find fitting locale, using fallback locale" << fallbackLocale;
749 c->setLocale(fallbackLocale);
750}
751
752void LangSelectPrivate::setContentLanguage(Context *c) const
753{
754 if (addContentLanguageHeader) {
755 c->res()->setHeader(QStringLiteral("Content-Language"), c->locale().bcp47Name());
756 }
757 c->stash({{langStashKey, c->locale().bcp47Name()},
758 {dirStashKey,
759 (c->locale().textDirection() == Qt::LeftToRight ? QStringLiteral("ltr")
760 : QStringLiteral("rtl"))}});
761}
762
763void LangSelectPrivate::beforePrepareAction(Context *c, bool *skipMethod) const
764{
765 if (*skipMethod) {
766 return;
767 }
768
769 if (!c->stash(SELECTION_TRIED).isNull()) {
770 return;
771 }
772
773 detectLocale(c, source, skipMethod);
774
775 c->setStash(SELECTION_TRIED, true);
776}
777
778void LangSelectPrivate::_q_postFork(Application *app)
779{
780 lsp = app->plugin<LangSelect *>();
781}
782
783#include "moc_langselect.cpp"
The Cutelyst Application.
Definition application.h:43
void beforePrepareAction(Cutelyst::Context *c, bool *skipMethod)
T plugin()
Returns the registered plugin that casts to the template type T.
void postForked(Cutelyst::Application *app)
The Cutelyst Context.
Definition context.h:39
void stash(const QVariantHash &unite)
Definition context.cpp:566
void detach(Action *action=nullptr)
Definition context.cpp:345
QLocale locale() const noexcept
Definition context.cpp:466
Response * res() const noexcept
Definition context.cpp:102
void setStash(const QString &key, const QVariant &value)
Definition context.cpp:217
void setLocale(const QLocale &locale)
Definition context.cpp:472
Language selection plugin.
Definition langselect.h:330
void setLocalesFromDir(const QString &path, const QString &name, const QString &prefix=QStringLiteral("."), const QString &suffix=QStringLiteral(".qm"))
void setDetectFromHeader(bool enabled)
void setLanguageDirStashKey(const QString &key=QStringLiteral("c_langselect_dir"))
static bool fromPath(Context *c, const QString &locale)
static QVector< QLocale > getSupportedLocales()
void setFallbackLocale(const QLocale &fallback)
void setQueryKey(const QString &key)
void setSubDomainMap(const QMap< QString, QLocale > &map)
static bool fromCookie(Context *c, const QString &name=QString())
static bool fromDomain(Context *c, const QMap< QString, QLocale > &domainMap=QMap< QString, QLocale >())
void setDomainMap(const QMap< QString, QLocale > &map)
void setLanguageCodeStashKey(const QString &key=QStringLiteral("c_langselect_lang"))
static bool fromUrlQuery(Context *c, const QString &key=QString())
void setCookieName(const QString &name)
static bool fromSession(Context *c, const QString &key=QString())
void setLocalesFromDirs(const QString &path, const QString &name)
static bool fromSubDomain(Context *c, const QMap< QString, QLocale > &subDomainMap=QMap< QString, QLocale >())
QVector< QLocale > supportedLocales() const
virtual ~LangSelect() override
void setSessionKey(const QString &key)
void addSupportedLocale(const QLocale &locale)
virtual bool setup(Application *app) override
void setSupportedLocales(const QVector< QLocale > &locales)
LangSelect(Application *parent, Source source)
Plugin(Application *parent)
Definition plugin.cpp:12
QString header(const QString &key) const
Definition request.h:581
QString queryParam(const QString &key, const QString &defaultValue={}) const
Definition request.h:561
QString cookie(const QString &name) const
Definition request.cpp:274
void redirect(const QUrl &url, quint16 status=Found)
Definition response.cpp:278
void setHeader(const QString &field, const QString &value)
Definition response.cpp:328
void setCookie(const QNetworkCookie &cookie)
Definition response.cpp:232
static QVariant value(Context *c, const QString &key, const QVariant &defaultValue=QVariant())
Definition session.cpp:170
static void setValue(Context *c, const QString &key, const QVariant &value)
Definition session.cpp:185
The Cutelyst namespace holds all public Cutelyst API.
Definition Mainpage.dox:8
QFileInfoList entryInfoList(Filters filters, SortFlags sort) const const
QStringList entryList(Filters filters, SortFlags sort) const const
bool exists() const const
int size() const const
QString bcp47Name() const const
Language language() const const
const_iterator constBegin() const const
const_iterator constEnd() const const
bool empty() const const
int size() const const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject * parent() const const
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
QString left(int n) const const
QString mid(int position, int n) const const
float toFloat(bool *ok) const const
QByteArray toLatin1() const const
QString toLower() const const
int indexOf(QStringView str, int from) const const
LeftToRight
SkipEmptyParts
QString host(ComponentFormattingOptions options) const const
QString path(ComponentFormattingOptions options) const const
QLocale toLocale() const const
int size() const const