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(QDir::Filters filters, QDir::SortFlags sort) const const
QStringList entryList(QDir::Filters filters, QDir::SortFlags sort) const const
bool exists() const const
qsizetype size() const const
QString bcp47Name(QLocale::TagSeparator separator) const const
QLocale::Language language() const const
QMap< Key, T >::const_iterator constBegin() const const
QMap< Key, T >::const_iterator constEnd() const const
bool empty() const const
QMap< Key, T >::size_type size() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QObject * parent() const const
bool isEmpty() const const
QString left(qsizetype n) &&
QString mid(qsizetype position, qsizetype n) &&
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
float toFloat(bool *ok) const const
QByteArray toLatin1() const const
QString toLower() const const
qsizetype indexOf(QLatin1StringView str, qsizetype from, Qt::CaseSensitivity cs) const const
LeftToRight
SkipEmptyParts
QString host(QUrl::ComponentFormattingOptions options) const const
QString path(QUrl::ComponentFormattingOptions options) const const
QLocale toLocale() const const