Skip to content

Commit 090c1c4

Browse files
committed
refactor: migrated contact page (#3834)
1 parent 8149e9e commit 090c1c4

File tree

11 files changed

+331
-136
lines changed

11 files changed

+331
-136
lines changed

.docker/nginx/default.conf

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ server {
7878
rewrite ^/add-question\.html$ /index.php?action=ask last;
7979
rewrite ^/show-categories\.html$ /index.php?action=show last;
8080
rewrite ^/forgot-password/?$ /index.php?action=password last;
81-
rewrite ^/(search|open-questions|help|contact|glossary|overview|login|privacy|index)\.html$ /index.php?action=$1 last;
81+
rewrite ^/(search|open-questions|help|glossary|overview|login|privacy|index)\.html$ /index.php?action=$1 last;
8282
rewrite ^/(login)$ /index.php?action=login last;
8383

8484
# a solution id page
@@ -259,7 +259,7 @@ server {
259259
rewrite ^/add-question\.html$ /index.php?action=ask last;
260260
rewrite ^/show-categories\.html$ /index.php?action=show last;
261261
rewrite ^/forgot-password/?$ /index.php?action=password last;
262-
rewrite ^/(search|open-questions|help|contact|glossary|overview|login|privacy|index)\.html$ /index.php?action=$1 last;
262+
rewrite ^/(search|open-questions|help|glossary|overview|login|privacy|index)\.html$ /index.php?action=$1 last;
263263
rewrite ^/(login)$ /index.php?action=login last;
264264

265265
# a solution id page

CLAUDE.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,50 @@ It is built using HTML5, CSS, TypeScript, and PHP and supports various databases
7070
- Use single quotes for strings.
7171
- Use arrow functions for callbacks.
7272

73+
## Routing System
74+
75+
The application uses Symfony Router for modern, controller-based routing while maintaining backward compatibility with legacy code.
76+
77+
### Architecture
78+
79+
1. **index.php**: Entry point that tries Symfony Router first, falls back to legacy logic
80+
2. **public-routes.php**: Route definitions using Symfony RouteCollection
81+
3. **Controllers**: Modern Controller classes extending AbstractController
82+
83+
### Adding New Routes
84+
85+
To add a new route:
86+
87+
1. Create a Controller in `phpmyfaq/src/phpMyFAQ/Controller/`
88+
2. Add the route to `phpmyfaq/src/public-routes.php`
89+
3. The Controller should extend `AbstractController`
90+
91+
Example:
92+
93+
```php
94+
// MyController.php
95+
final class MyController extends AbstractController
96+
{
97+
public function index(Request $request): Response
98+
{
99+
return $this->render('template.twig', ['data' => 'value']);
100+
}
101+
}
102+
103+
// public-routes.php
104+
'public.my_route' => [
105+
'path' => '/my-page.html',
106+
'controller' => [MyController::class, 'index'],
107+
'methods' => 'GET'
108+
]
109+
```
110+
111+
### Migration Strategy
112+
113+
- New features should use Controllers
114+
- Legacy code continues to work via fallback mechanism
115+
- Gradual migration from `?action=xyz` to route-based URLs
116+
73117
## UI guidelines
74118

75119
- Application should have a modern and clean design.

nginx.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ server {
100100
rewrite ^/add-question\.html$ /index.php?action=ask last;
101101
rewrite ^/show-categories\.html$ /index.php?action=show last;
102102
rewrite ^/forgot-password/?$ /index.php?action=password last;
103-
rewrite ^/(search|open-questions|help|contact|glossary|overview|login|privacy|index)\.html$ /index.php?action=$1 last;
103+
rewrite ^/(search|open-questions|help|glossary|overview|login|privacy|index)\.html$ /index.php?action=$1 last;
104104
rewrite ^/(login)$ /index.php?action=login last;
105105

106106
# a solution id page

phpmyfaq/.htaccess

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ Header set Access-Control-Allow-Headers "Content-Type, Authorization"
109109
RewriteRule ^add-question.html$ index.php?action=ask [L,QSA]
110110
RewriteRule ^show-categories.html$ index.php?action=show [L,QSA]
111111
RewriteRule ^forgot-password$ index.php?action=password [L,QSA]
112-
RewriteRule ^(search|open-questions|contact|glossary|overview|login|privacy)\.html$ index.php?action=$1 [L,QSA]
112+
RewriteRule ^(search|open-questions|glossary|overview|login|privacy)\.html$ index.php?action=$1 [L,QSA]
113113
RewriteRule ^(login) index.php?action=login [L,QSA]
114114
# start page
115115
RewriteRule ^index.html$ index.php [L,QSA]

phpmyfaq/contact.php

Lines changed: 0 additions & 64 deletions
This file was deleted.

phpmyfaq/index.php

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@
5151
use Symfony\Component\HttpFoundation\RedirectResponse;
5252
use Symfony\Component\HttpFoundation\Request;
5353
use Symfony\Component\HttpFoundation\Response;
54+
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
55+
use Symfony\Component\Routing\Matcher\UrlMatcher;
56+
use Symfony\Component\Routing\RequestContext;
5457

5558
//
5659
// Define the named constant used as a check by any included PHP file
@@ -114,6 +117,41 @@
114117
//
115118
Strings::init($faqLangCode);
116119

120+
//
121+
// Try Symfony Router first
122+
//
123+
try {
124+
// Load routes
125+
$routes = require __DIR__ . '/src/public-routes.php';
126+
127+
// Create URL matcher
128+
$context = new RequestContext();
129+
$context->fromRequest($request);
130+
$matcher = new UrlMatcher($routes, $context);
131+
132+
// Try to match the current route
133+
$parameters = $matcher->match($request->getPathInfo());
134+
135+
// Extract controller and method
136+
$controllerCallable = $parameters['_controller'];
137+
unset($parameters['_controller'], $parameters['_route'], $parameters['_methods']);
138+
139+
// Instantiate controller and call method
140+
if (is_array($controllerCallable)) {
141+
[$controllerClass, $method] = $controllerCallable;
142+
$controller = new $controllerClass();
143+
$routeResponse = $controller->$method($request, ...$parameters);
144+
} else {
145+
$routeResponse = $controllerCallable($request, ...$parameters);
146+
}
147+
148+
// Send response and exit
149+
$routeResponse->send();
150+
exit;
151+
} catch (ResourceNotFoundException $e) {
152+
// No route matched - continue with legacy logic below
153+
}
154+
117155
//
118156
// Set actual template set name
119157
//
@@ -618,7 +656,7 @@
618656
];
619657

620658
//
621-
// Show login box or logged-in user information
659+
// Show the login box or logged-in user information
622660
//
623661
if ($user->isLoggedIn() && $user->getUserId() > 0) {
624662
if (
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
<?php
2+
3+
/**
4+
* The abstract Front Controller
5+
*
6+
* This Source Code Form is subject to the terms of the Mozilla Public License,
7+
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
8+
* obtain one at https://mozilla.org/MPL/2.0/.
9+
*
10+
* @package phpMyFAQ
11+
* @author Thorsten Rinne <[email protected]>
12+
* @copyright 2024-2025 phpMyFAQ Team
13+
* @license https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
14+
* @link https://www.phpmyfaq.de
15+
* @since 2024-06-16
16+
*/
17+
18+
declare(strict_types=1);
19+
20+
namespace phpMyFAQ\Controller;
21+
22+
use phpMyFAQ\Core\Exception;
23+
use phpMyFAQ\Enums\PermissionType;
24+
use phpMyFAQ\Environment;
25+
use phpMyFAQ\Helper\LanguageHelper;
26+
use phpMyFAQ\System;
27+
use phpMyFAQ\Translation;
28+
use phpMyFAQ\Twig\TwigWrapper;
29+
use Symfony\Component\HttpFoundation\Request;
30+
31+
abstract class AbstractFrontController extends AbstractController
32+
{
33+
/**
34+
* @return string[]
35+
* @throws Exception
36+
* @throws \Exception
37+
*/
38+
protected function getHeader(Request $request): array
39+
{
40+
$faqSystem = $this->container->get(id: 'phpmyfaq.system');
41+
$seo = $this->container->get(id: 'phpmyfaq.seo');
42+
$action = $request->query->get(key: 'action', default: 'index');
43+
44+
$isUserHasAdminRights = $this->currentUser->perm->hasPermission(
45+
$this->currentUser->getUserId(),
46+
PermissionType::VIEW_ADMIN_LINK->value,
47+
);
48+
49+
return [
50+
'isMaintenanceMode' => $this->configuration->get('main.maintenanceMode'),
51+
'isCompletelySecured' => $this->configuration->get('security.enableLoginOnly'),
52+
'isDebugEnabled' => Environment::isDebugMode(),
53+
'richSnippetsEnabled' => $this->configuration->get('seo.enableRichSnippets'),
54+
'tplSetName' => TwigWrapper::getTemplateSetName(),
55+
'msgLoginUser' => $this->currentUser->isLoggedIn()
56+
? $this->currentUser->getUserData('display_name')
57+
: Translation::get(key: 'msgLoginUser'),
58+
'isUserLoggedIn' => $this->currentUser->isLoggedIn(),
59+
'isUserHasAdminRights' => $isUserHasAdminRights || $this->currentUser->isSuperAdmin(),
60+
'baseHref' => $faqSystem->getSystemUri($this->configuration),
61+
'customCss' => $this->configuration->getCustomCss(),
62+
'version' => $this->configuration->getVersion(),
63+
'header' => str_replace('"', '', $this->configuration->getTitle()),
64+
'metaDescription' => $metaDescription ?? $this->configuration->get('seo.description'),
65+
'metaPublisher' => $this->configuration->get('main.metaPublisher'),
66+
'metaLanguage' => Translation::get(key: 'metaLanguage'),
67+
'metaRobots' => $seo->getMetaRobots($action),
68+
'phpmyfaqVersion' => $this->configuration->getVersion(),
69+
'stylesheet' => Translation::get(key: 'direction') == 'rtl' ? 'style.rtl' : 'style',
70+
'currentPageUrl' => $request->getSchemeAndHttpHost() . $request->getRequestUri(),
71+
'action' => $action,
72+
'dir' => Translation::get(key: 'direction'),
73+
'formActionUrl' => './search',
74+
'searchBox' => Translation::get(key: 'msgSearch'),
75+
'languageBox' => Translation::get(key: 'msgLanguageSubmit'),
76+
'switchLanguages' => LanguageHelper::renderSelectLanguage(
77+
$this->configuration->getLanguage()->getLanguage(),
78+
true,
79+
),
80+
'copyright' => System::getPoweredByString(),
81+
'isUserRegistrationEnabled' => $this->configuration->get('security.enableRegistration'),
82+
'pluginStylesheets' => $this->configuration->getPluginManager()->getAllPluginStylesheets(),
83+
'pluginScripts' => $this->configuration->getPluginManager()->getAllPluginScripts(),
84+
'msgRegisterUser' => Translation::get(key: 'msgRegisterUser'),
85+
'sendPassword' =>
86+
'<a href="'
87+
. $faqSystem->getSystemUri($this->configuration)
88+
. 'forgot-password">'
89+
. Translation::get(key: 'lostPassword')
90+
. '</a>',
91+
'msgFullName' => Translation::get(key: 'ad_user_loggedin') . $this->currentUser->getLogin(),
92+
'msgLoginName' => $this->currentUser->getUserData('display_name'),
93+
'loginHeader' => Translation::get(key: 'msgLoginUser'),
94+
'msgAdvancedSearch' => Translation::get(key: 'msgAdvancedSearch'),
95+
'currentYear' => date(format: 'Y', timestamp: time()),
96+
'cookieConsentEnabled' => $this->configuration->get('layout.enableCookieConsent'),
97+
'faqHome' => $this->configuration->getDefaultUrl(),
98+
'topNavigation' => $this->getTopNavigation($request),
99+
'isAskQuestionsEnabled' => $this->configuration->get('main.enableAskQuestions'),
100+
'isOpenQuestionsEnabled' => $this->configuration->get('main.enableAskQuestions'),
101+
'footerNavigation' => $this->getFooterNavigation($request),
102+
'isPrivacyLinkEnabled' => $this->configuration->get('layout.enablePrivacyLink'),
103+
'urlPrivacyLink' => $this->configuration->get('main.privacyURL'),
104+
'msgPrivacyNote' => Translation::get(key: 'msgPrivacyNote'),
105+
'isCookieConsentEnabled' => $this->configuration->get('layout.enableCookieConsent'),
106+
'cookiePreferences' => Translation::get(key: 'cookiePreferences'),
107+
];
108+
}
109+
110+
private function getTopNavigation(Request $request): array
111+
{
112+
$action = $request->query->get(key: 'action', default: 'index');
113+
114+
return [
115+
[
116+
'name' => Translation::get(key: 'msgShowAllCategories'),
117+
'link' => './show-categories.html',
118+
'active' => 'show' === $action ? 'active' : '',
119+
],
120+
[
121+
'name' => Translation::get(key: 'msgAddContent'),
122+
'link' => './add-faq.html',
123+
'active' => 'add' === $action ? 'active' : '',
124+
],
125+
[
126+
'name' => Translation::get(key: 'msgQuestion'),
127+
'link' => './add-question.html',
128+
'active' => 'ask' == $action ? 'active' : '',
129+
],
130+
[
131+
'name' => Translation::get(key: 'msgOpenQuestions'),
132+
'link' => './open-questions.html',
133+
'active' => 'open-questions' == $action ? 'active' : '',
134+
],
135+
];
136+
}
137+
138+
private function getFooterNavigation(Request $request): array
139+
{
140+
$action = $request->query->get(key: 'action', default: 'index');
141+
142+
return [
143+
[
144+
'name' => Translation::get(key: 'faqOverview'),
145+
'link' => './overview.html',
146+
'active' => 'faq-overview' == $action ? 'active' : '',
147+
],
148+
[
149+
'name' => Translation::get(key: 'msgSitemap'),
150+
'link' => './sitemap/A/' . $this->configuration->getLanguage()->getLanguage() . '.html',
151+
'active' => 'sitemap' == $action ? 'active' : '',
152+
],
153+
[
154+
'name' => Translation::get(key: 'ad_menu_glossary'),
155+
'link' => './glossary.html',
156+
'active' => 'glossary' == $action ? 'active' : '',
157+
],
158+
[
159+
'name' => Translation::get(key: 'msgContact'),
160+
'link' => './contact.html',
161+
'active' => 'contact' == $action ? 'active' : '',
162+
],
163+
];
164+
}
165+
}

0 commit comments

Comments
 (0)