Skip to content

Commit

Permalink
Merge branch '4.0' into 'main'
Browse files Browse the repository at this point in the history
  • Loading branch information
thorsten committed Jan 8, 2025
2 parents 6057de2 + 96f46f6 commit 5834f8b
Show file tree
Hide file tree
Showing 15 changed files with 140 additions and 112 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ This is a log of major user-visible changes in each phpMyFAQ release.
- migrated from Webpack to Vite v6 (Thorsten)
- migrated from Jest to vitest (Thorsten)

### phpMyFAQ v4.0.4 - unreleased

- improved update from v3 (Thorsten)
- updated third party dependencies (Thorsten)
- fixed minor bugs (Thorsten)

### phpMyFAQ v4.0.3 - 2025-01-03

- fixed installation bug introduced with v4.0.2 (Thorsten)
Expand Down
2 changes: 2 additions & 0 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ To install it, you will need a web server that meets the following requirements:
- Filter support
- SPL support
- FileInfo support
- Sodium support

### Web server requirements

Expand All @@ -28,6 +29,7 @@ You can use phpMyFAQ with the following web servers:

- mod_rewrite
- mod_ssl (if you wish to run phpMyFAQ under SSL)
- mod_headers

You should also ensure you have `AllowOverride All` set in the `<Directory>` and/or `<VirtualHost>` blocks so
that the `.htaccess` file processes correctly, and rewrite rules take effect. Please check, if your path in
Expand Down
3 changes: 3 additions & 0 deletions phpmyfaq/admin/assets/src/user/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ const setUserData = async (userId) => {
element.removeAttribute('disabled');
});
document.getElementById('checkAll').removeAttribute('disabled');
document.getElementById('uncheckAll').setAttribute('disabled', '');
} else {
const superAdmin = document.getElementById('is_superadmin');
superAdmin.removeAttribute('checked');
Expand Down Expand Up @@ -151,6 +152,7 @@ export const handleUsers = async () => {
element.removeAttribute('disabled');
});
document.getElementById('checkAll').setAttribute('disabled', '');
document.getElementById('uncheckAll').setAttribute('disabled', '');
} else {
document.querySelectorAll('.permission').forEach((checkbox) => {
checkbox.removeAttribute('disabled');
Expand All @@ -159,6 +161,7 @@ export const handleUsers = async () => {
element.removeAttribute('disabled');
});
document.getElementById('checkAll').removeAttribute('disabled');
document.getElementById('uncheckAll').removeAttribute('disabled');
}
});
}
Expand Down
60 changes: 17 additions & 43 deletions phpmyfaq/assets/src/configuration/update.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,51 +111,25 @@ export const handleDatabaseUpdate = async () => {
body: installedVersion.value,
});

const result = await response.json();
const progressBarInstallation = document.getElementById('result-update');
const reader = response.body.getReader();

async function pump() {
const { done, value } = await reader.read();
const decodedValue = new TextDecoder().decode(value);
if (done) {
progressBarInstallation.style.width = '100%';
progressBarInstallation.innerText = '100%';
progressBarInstallation.classList.remove('progress-bar-animated');
const alert = document.getElementById('phpmyfaq-update-database-success');
alert.classList.remove('d-none');
return;
} else {
let value;
try {
value = JSON.parse(decodedValue);
} catch (error) {
console.error('Failed to parse JSON:', error);
const alert = document.getElementById('phpmyfaq-update-database-error');
const errorMessage = document.getElementById('error-messages');
alert.classList.remove('d-none');
errorMessage.innerText = `Error: ${error.message}\nFull Error: ${decodedValue}`;
return;
}
if (value.progress) {
progressBarInstallation.style.width = value.progress;
progressBarInstallation.innerText = value.progress;
}
if (value.error) {
progressBarInstallation.style.width = '100%';
progressBarInstallation.innerText = '100%';
progressBarInstallation.classList.remove('progress-bar-animated');
const alert = document.getElementById('phpmyfaq-update-database-error');
const errorMessage = document.getElementById('error-messages');
alert.classList.remove('d-none');
errorMessage.innerHTML = value.error;
return;
}
}

await pump();
}

await pump();
if (response.ok) {
progressBarInstallation.style.width = '100%';
progressBarInstallation.innerText = '100%';
progressBarInstallation.classList.remove('progress-bar-animated');
const alert = document.getElementById('phpmyfaq-update-database-success');
alert.classList.remove('d-none');
alert.innerText = result.success;
} else {
progressBarInstallation.style.width = '100%';
progressBarInstallation.innerText = '100%';
progressBarInstallation.classList.remove('progress-bar-animated');
const alert = document.getElementById('phpmyfaq-update-database-error');
const errorMessage = document.getElementById('error-messages');
alert.classList.remove('d-none');
errorMessage.innerHTML = result.error;
}
} catch (error) {
console.error('Error details:', error);
const alert = document.getElementById('phpmyfaq-update-database-error');
Expand Down
39 changes: 39 additions & 0 deletions phpmyfaq/assets/templates/default/twofactor.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{% extends 'index.twig' %}

{% block content %}
<div class="col-12">
{{ loginMessage }}
<div class="container py-5">
<div class="row">
<div class="col-lg-12">
<div class="row">
<div class="col-lg-5 mx-auto">
<div class="card rounded-0" id="login-form">
<div class="card-header">
<h3 class="mb-0">{{ msgTwofactorEnabled }}</h3>
</div>
<div class="card-body">
<form action="{{ writeLoginPath }}" method="post" accept-charset="utf-8" role="form" class="form">
<input type="hidden" id="userid" name="userid" value="{{ userid }}">
<div class="row mb-2">
<label for="token">{{ msgEnterTwofactorToken }}</label>
<div class="col-4 mx-auto my-2">
<input type="text" class="form-control form-control-lg text-center rounded-0" name="token"
id="token" autocomplete="off" maxlength="6" autofocus required>
</div>
</div>
<div class="d-grid gap-2 col-6 mx-auto">
<button type="submit" class="btn btn-success btn-lg float-right" id="btnLogin">
{{ msgTwofactorCheck }}
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
3 changes: 2 additions & 1 deletion phpmyfaq/assets/templates/setup/update/step1.twig
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<form action="#" method="post" name="phpmyfaq-update-form" id="phpmyfaq-update-form">
<form action="#" method="post" name="phpmyfaq-update-form" id="phpmyfaq-update-form" accept-charset="UTF-8"
enctype="application/x-www-form-urlencoded">
<input name="installed-version" id="phpmyfaq-update-installed-version" type="hidden" value="{{ installedVersion }}">
<input name="next-step" id="phpmyfaq-update-next-step" type="hidden" value="2">

Expand Down
3 changes: 2 additions & 1 deletion phpmyfaq/assets/templates/setup/update/step2.twig
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<form action="#" method="post" name="phpmyfaq-update-form" id="phpmyfaq-update-form">
<form action="#" method="post" name="phpmyfaq-update-form" id="phpmyfaq-update-form" accept-charset="UTF-8"
enctype="application/x-www-form-urlencoded">
<input name="installed-version" id="phpmyfaq-update-installed-version" type="hidden" value="{{ installedVersion }}">
<input name="next-step" id="phpmyfaq-update-next-step" type="hidden" value="3">

Expand Down
3 changes: 2 additions & 1 deletion phpmyfaq/assets/templates/setup/update/step3.twig
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<form action="#" method="post" name="phpmyfaq-update-form" id="phpmyfaq-update-form">
<form action="#" method="post" name="phpmyfaq-update-form" id="phpmyfaq-update-form" accept-charset="UTF-8"
enctype="application/x-www-form-urlencoded">
<input name="installed-version" id="phpmyfaq-update-installed-version" type="hidden" value="{{ installedVersion }}">

<div class="row">
Expand Down
4 changes: 3 additions & 1 deletion phpmyfaq/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@
$request = Request::createFromGlobals();
$response = new Response();
$response->headers->set('Content-Type', 'text/html');
$csrfLogoutToken = Token::getInstance()->getTokenString('logout');


//
// Service Containers
Expand Down Expand Up @@ -638,7 +640,7 @@
'msgBookmarks' => Translation::get('msgBookmarks'),
'msgUserRemoval' => Translation::get('ad_menu_RequestRemove'),
'msgLogoutUser' => Translation::get('ad_menu_logout'),
'csrfLogout' => Token::getInstance($container->get('session'))->getTokenString('logout'),
'csrfLogout' => $csrfLogoutToken,
];
}

Expand Down
6 changes: 5 additions & 1 deletion phpmyfaq/login.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,13 @@
$loginMessage = '<div class="alert alert-danger" role="alert">' . $error . '</div>';
}

$templateFile = './login.twig';
if ($action == 'twofactor') {
$templateFile = './twofactor.twig';
}

$twig = new TwigWrapper(PMF_ROOT_DIR . '/assets/templates/');
$twigTemplate = $twig->loadTemplate('./login.twig');
$twigTemplate = $twig->loadTemplate($templateFile);

$templateVars = [
... $templateVars,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@
use phpMyFAQ\Core\Exception;
use phpMyFAQ\Enums\PermissionType;
use phpMyFAQ\Filter;
use phpMyFAQ\Setup\Update;
use phpMyFAQ\Setup\Upgrade;
use phpMyFAQ\System;
use phpMyFAQ\Translation;
use Symfony\Component\HttpClient\HttpClient;
Expand All @@ -43,7 +41,7 @@
class UpdateController extends AbstractController
{
/**
* @throws Exception
* @throws Exception|\Exception
*/
#[Route('admin/api/health-check')]
public function healthCheck(): JsonResponse
Expand All @@ -52,7 +50,7 @@ public function healthCheck(): JsonResponse

$dateTime = new DateTime();
$dateLastChecked = $dateTime->format(DateTimeInterface::ATOM);
$upgrade = new Upgrade(new System(), $this->configuration);
$upgrade = $this->container->get('phpmyfaq.setup.upgrade');

if (!$upgrade->isMaintenanceEnabled()) {
return $this->json(
Expand Down Expand Up @@ -84,9 +82,6 @@ public function healthCheck(): JsonResponse
}
}

/**
* @throws Exception
*/
#[Route('admin/api/versions')]
public function versions(): JsonResponse
{
Expand All @@ -109,7 +104,7 @@ public function versions(): JsonResponse
}

/**
* @throws Exception
* @throws Exception|\Exception
*/
#[Route('admin/api/update-check')]
public function updateCheck(): JsonResponse
Expand Down Expand Up @@ -155,7 +150,7 @@ public function updateCheck(): JsonResponse
* @throws RedirectionExceptionInterface
* @throws ClientExceptionInterface
* @throws \JsonException
* @throws Exception
* @throws Exception|\Exception
*/
#[Route('admin/api/download-package')]
public function downloadPackage(Request $request): JsonResponse
Expand All @@ -164,7 +159,7 @@ public function downloadPackage(Request $request): JsonResponse

$versionNumber = Filter::filterVar($request->get('versionNumber'), FILTER_SANITIZE_SPECIAL_CHARS);

$upgrade = new Upgrade(new System(), $this->configuration);
$upgrade = $this->container->get('phpmyfaq.setup.upgrade');
$pathToPackage = $upgrade->downloadPackage($versionNumber);

if ($pathToPackage === false) {
Expand All @@ -188,7 +183,7 @@ public function extractPackage(): StreamedResponse
{
$this->userHasPermission(PermissionType::CONFIGURATION_EDIT);

$upgrade = new Upgrade(new System(), $this->configuration);
$upgrade = $this->container->get('phpmyfaq.setup.upgrade');
$pathToPackage = urldecode((string) $this->configuration->get('upgrade.lastDownloadedPackage'));

return new StreamedResponse(static function () use ($upgrade, $pathToPackage) {
Expand All @@ -210,7 +205,7 @@ public function createTemporaryBackup(): StreamedResponse
{
$this->userHasPermission(PermissionType::CONFIGURATION_EDIT);

$upgrade = new Upgrade(new System(), $this->configuration);
$upgrade = $this->container->get('phpmyfaq.setup.upgrade');
$backupHash = md5(uniqid());

return new StreamedResponse(static function () use ($upgrade, $backupHash) {
Expand All @@ -232,7 +227,7 @@ public function installPackage(): StreamedResponse
{
$this->userHasPermission(PermissionType::CONFIGURATION_EDIT);

$upgrade = new Upgrade(new System(), $this->configuration);
$upgrade = $this->container->get('phpmyfaq.setup.upgrade');
$configurator = $this->container->get('phpmyfaq.setup.environment_configurator');
return new StreamedResponse(static function () use ($upgrade, $configurator) {
$progressCallback = static function ($progress) {
Expand All @@ -249,39 +244,40 @@ public function installPackage(): StreamedResponse
}

#[Route('admin/api/update-database')]
public function updateDatabase(): StreamedResponse
public function updateDatabase(): JsonResponse
{
$this->userHasPermission(PermissionType::CONFIGURATION_EDIT);

$update = new Update(new System(), $this->configuration);
$update = $this->container->get('phpmyfaq.setup.update');
$update->setVersion(System::getVersion());

return new StreamedResponse(static function () use ($update) {
$progressCallback = static function ($progress) {
echo json_encode(['progress' => $progress]) . "\n";
ob_flush();
flush();
};
try {
if ($update->applyUpdates($progressCallback)) {
$this->configuration->set('main.maintenanceMode', 'false');
echo json_encode(['message' => '✅ Database successfully updated.']);
}
} catch (Exception $exception) {
echo json_encode(['message' => 'Update database failed: ' . $exception->getMessage()]);
try {
if ($update->applyUpdates()) {
$this->configuration->set('main.maintenanceMode', 'false');
return new JsonResponse(
['success' => '✅ Database successfully updated.'],
Response::HTTP_OK
);
}
});

return new JsonResponse(['error' => 'Update database failed.'], Response::HTTP_BAD_GATEWAY);
} catch (Exception $exception) {
return new JsonResponse(
['error' => 'Update database failed: ' . $exception->getMessage()],
Response::HTTP_BAD_GATEWAY
);
}
}

/**
* @throws Exception
* @throws Exception|\Exception
*/
#[Route('admin/api/cleanup')]
public function cleanUp(): JsonResponse
{
$this->userHasPermission(PermissionType::CONFIGURATION_EDIT);

$upgrade = new Upgrade(new System(), $this->configuration);
$upgrade = $this->container->get('phpmyfaq.setup.upgrade');
$upgrade->cleanUp();

return $this->json(['message' => '✅ Cleanup successful.'], Response::HTTP_OK);
Expand Down
Loading

0 comments on commit 5834f8b

Please sign in to comment.