Skip to content

Commit

Permalink
Introduce page to display in case of HTTP 404
Browse files Browse the repository at this point in the history
  • Loading branch information
Quetzacoalt91 committed Jan 9, 2025
1 parent aeeee3d commit 8ae36dc
Show file tree
Hide file tree
Showing 13 changed files with 386 additions and 75 deletions.
17 changes: 13 additions & 4 deletions _dev/src/ts/api/RequestHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,25 @@ export class RequestHandler {
data.append('dir', window.AutoUpgradeVariables.admin_dir);

try {
const response = await baseApi.post('', data, {
const response = await baseApi.post<ApiResponse>('', data, {
params: { route },
signal
});

const responseData = response.data as ApiResponse;
const responseData = response.data;
await this.#handleResponse(responseData, fromPopState);
} catch (error) {
// TODO: catch errors
console.error(error);
// A couple or errors are returned in an actual response (i.e 404 or 500)
if (error instanceof AxiosError) {
if (error.response?.data) {
const responseData = error.response.data;
responseData.new_route = 'error-page';
await this.#handleResponse(responseData, true);
}
} else {
// TODO: catch errors
console.error(error);
}
}
}

Expand Down
23 changes: 23 additions & 0 deletions _dev/src/ts/pages/ErrorPage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import DomLifecycle from '../types/DomLifecycle';
import ErrorPage404 from './error/ErrorPage404';
import PageAbstract from './PageAbstract';

export default class ErrorPage extends PageAbstract {
errorPage?: DomLifecycle;

constructor() {
super();

if (document.getElementById('ua_error_404')) {
this.errorPage = new ErrorPage404();
}
}

public mount = () => {
this.errorPage?.mount();
};

public beforeDestroy = () => {
this.errorPage?.beforeDestroy();
};
}
53 changes: 53 additions & 0 deletions _dev/src/ts/pages/error/ErrorPage404.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import api from '../../api/RequestHandler';
import DomLifecycle from '../../types/DomLifecycle';

export default class ErrorPage404 implements DomLifecycle {
isOnHomePage: boolean = false;

public constructor() {
this.isOnHomePage = new URLSearchParams(window.location.search).get('route') === 'home-page';
}

public mount = () => {
this.#activeActionButton.classList.remove('hidden');
this.#form.addEventListener('submit', this.#onSubmit);
};

public beforeDestroy = () => {
this.#form.removeEventListener('submit', this.#onSubmit);
};

get #activeActionButton(): HTMLFormElement | HTMLAnchorElement {
return this.isOnHomePage ? this.#form : this.#exitButton;
}

get #form(): HTMLFormElement {
const form = document.forms.namedItem('home-page-form');
if (!form) {
throw new Error('Form not found');
}

['routeToSubmit'].forEach((data) => {
if (!form.dataset[data]) {
throw new Error(`Missing data ${data} from form dataset.`);
}
});

return form;
}

get #exitButton(): HTMLAnchorElement {
const link = document.getElementById('exit-button');

if (!link || !(link instanceof HTMLAnchorElement)) {
throw new Error('Link is not found or invalid');
}
return link;
}

readonly #onSubmit = async (event: Event) => {
event.preventDefault();

await api.post(this.#form.dataset.routeToSubmit!, new FormData(this.#form));
};
}
23 changes: 14 additions & 9 deletions _dev/src/ts/routing/ScriptHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import SendErrorReportDialog from '../dialogs/SendErrorReportDialog';
import DomLifecycle from '../types/DomLifecycle';
import { RoutesMatching } from '../types/scriptHandlerTypes';
import { routeHandler } from '../autoUpgrade';
import ErrorPage from '../pages/ErrorPage';

export default class ScriptHandler {
#currentScript: DomLifecycle | undefined;
Expand All @@ -38,6 +39,8 @@ export default class ScriptHandler {
'restore-page-restore': RestorePageRestore,
'restore-page-post-restore': RestorePagePostRestore,

'error-page': ErrorPage,

'start-update-dialog': StartUpdateDialog,
'send-error-report-dialog': SendErrorReportDialog
};
Expand All @@ -61,16 +64,18 @@ export default class ScriptHandler {
* @description Loads and mounts the page script associated with the specified route name.
*/
loadScript(scriptID: string) {
const classScript = this.#routesMatching[scriptID];
if (classScript) {
try {
this.#currentScript = new classScript();
this.#currentScript.mount();
} catch (error) {
console.error(`Failed to load script with ID ${scriptID}:`, error);
}
} else {
let classScript = this.#routesMatching[scriptID];
if (!classScript) {
console.debug(`No matching class found for ID: ${scriptID}`);
// Each route must provide a script to load. If it does not exist, we load the error management script
classScript = this.#routesMatching['error-page'];
}

try {
this.#currentScript = new classScript();
this.#currentScript.mount();
} catch (error) {
console.error(`Failed to load script with ID ${scriptID}:`, error);
}
}

Expand Down
74 changes: 74 additions & 0 deletions classes/Router/UrlGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php

/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License 3.0 (AFL-3.0)
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to [email protected] so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to https://devdocs.prestashop.com/ for more information.
*
* @author PrestaShop SA and Contributors <[email protected]>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
*/

namespace PrestaShop\Module\AutoUpgrade\Router;

use Symfony\Component\HttpFoundation\Request;

class UrlGenerator
{
/** @var string */
private $shopBasePath;
/** @var string */
private $adminFolder;

public function __construct(string $shopBasePath, string $adminFolder)
{
$this->shopBasePath = $shopBasePath;
$this->adminFolder = $adminFolder;
}

public function getShopAbsolutePathFromRequest(Request $request): string
{
// Determine the subdirectories of the PHP entry point (the script being executed)
// relative to the shop root folder.
// This calculation helps generate a base path that correctly accounts for any subfolder in which
// the shop might be installed.
$subDirs = explode(
DIRECTORY_SEPARATOR,
trim(
str_replace(
$this->shopBasePath,
'',
dirname($request->server->get('SCRIPT_FILENAME', '')
)
), DIRECTORY_SEPARATOR)
);
$numberOfSubDirs = count($subDirs);

$path = explode('/', $request->getBasePath());

$path = array_splice($path, 0, -$numberOfSubDirs);

return implode('/', $path) ?: '/';
}

public function getShopAdminAbsolutePathFromRequest(Request $request): string
{
return rtrim($this->getShopAbsolutePathFromRequest($request), '/') . '/' . $this->adminFolder;
}
}
36 changes: 6 additions & 30 deletions classes/Twig/AssetsEnvironment.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,19 @@

namespace PrestaShop\Module\AutoUpgrade\Twig;

use PrestaShop\Module\AutoUpgrade\Router\UrlGenerator;
use Symfony\Component\HttpFoundation\Request;

class AssetsEnvironment
{
const DEV_BASE_URL = 'http://localhost:5173';

/** @var string */
private $shopBasePath;
/** @var UrlGenerator */
protected $urlGenerator;

public function __construct(string $shopBasePath)
public function __construct(UrlGenerator $urlGenerator)
{
$this->shopBasePath = $shopBasePath;
$this->urlGenerator = $urlGenerator;
}

public function isDevMode(): bool
Expand All @@ -52,31 +53,6 @@ public function getAssetsBaseUrl(Request $request): string
return self::DEV_BASE_URL;
}

return $this->getShopUrlFromRequest($request) . '/modules/autoupgrade/views';
}

private function getShopUrlFromRequest(Request $request): string
{
// Determine the subdirectories of the PHP entry point (the script being executed)
// relative to the shop root folder.
// This calculation helps generate a base path that correctly accounts for any subfolder in which
// the shop might be installed.
$subDirs = explode(
DIRECTORY_SEPARATOR,
trim(
str_replace(
$this->shopBasePath,
'',
dirname($request->server->get('SCRIPT_FILENAME', '')
)
), DIRECTORY_SEPARATOR)
);
$numberOfSubDirs = count($subDirs);

$path = explode('/', $request->getBasePath());

$path = array_splice($path, 0, -$numberOfSubDirs);

return implode('/', $path);
return rtrim($this->urlGenerator->getShopAbsolutePathFromRequest($request), '/') . '/modules/autoupgrade/views';
}
}
25 changes: 22 additions & 3 deletions classes/UpgradeContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
use PrestaShop\Module\AutoUpgrade\Parameters\UpgradeConfiguration;
use PrestaShop\Module\AutoUpgrade\Progress\CompletionCalculator;
use PrestaShop\Module\AutoUpgrade\Repository\LocalArchiveRepository;
use PrestaShop\Module\AutoUpgrade\Router\UrlGenerator;
use PrestaShop\Module\AutoUpgrade\Services\ComposerService;
use PrestaShop\Module\AutoUpgrade\Services\DistributionApiService;
use PrestaShop\Module\AutoUpgrade\Services\LogsService;
Expand Down Expand Up @@ -269,6 +270,9 @@ class UpgradeContainer
*/
private $upgradeSelfCheck;

/** @var UrlGenerator */
private $urlGenerator;

/**
* @var PhpVersionResolverService
*/
Expand Down Expand Up @@ -943,11 +947,11 @@ public function getLocalArchiveRepository(): LocalArchiveRepository
*/
public function getAssetsEnvironment(): AssetsEnvironment
{
if (null !== $this->assetsEnvironment) {
return $this->assetsEnvironment;
if (null === $this->assetsEnvironment) {
$this->assetsEnvironment = new AssetsEnvironment($this->getUrlGenerator());
}

return $this->assetsEnvironment = new AssetsEnvironment($this->getProperty(self::PS_ROOT_PATH));
return $this->assetsEnvironment;
}

/**
Expand Down Expand Up @@ -992,6 +996,21 @@ public function getPrestashopVersionService(): PrestashopVersionService
return $this->prestashopVersionService = new PrestashopVersionService($this->getZipAction());
}

/**
* @return UrlGenerator
*/
public function getUrlGenerator(): UrlGenerator
{
if (null === $this->urlGenerator) {
$this->urlGenerator = new UrlGenerator(
$this->getProperty(self::PS_ROOT_PATH),
$this->getProperty(self::PS_ADMIN_SUBDIR),
);
}

return $this->urlGenerator;
}

/**
* Checks if the composer autoload exists, and loads it.
*
Expand Down
25 changes: 24 additions & 1 deletion controllers/admin/self-managed/Error404Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,41 @@

namespace PrestaShop\Module\AutoUpgrade\Controller;

use PrestaShop\Module\AutoUpgrade\Router\Routes;
use Symfony\Component\HttpFoundation\Response;

class Error404Controller extends AbstractPageController
{
const ERROR_CODE = 404;

public function index()
{
$response = parent::index();

if ($response instanceof Response) {
$response->setStatusCode(self::ERROR_CODE);
} else {
http_response_code(self::ERROR_CODE);
}

return $response;
}

protected function getPageTemplate(): string
{
return 'errors/404';
return 'errors/' . self::ERROR_CODE;
}

protected function getParams(): array
{
return [
// TODO: assets_base_path is provided by all controllers. What about a asset() twig function instead?
'assets_base_path' => $this->upgradeContainer->getAssetsEnvironment()->getAssetsBaseUrl($this->request),

'error_code' => self::ERROR_CODE,

'exit_to_shop_admin' => $this->upgradeContainer->getUrlGenerator()->getShopAdminAbsolutePathFromRequest($this->request),
'exit_to_app_home' => Routes::HOME_PAGE,
];
}
}
Loading

0 comments on commit 8ae36dc

Please sign in to comment.