Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Make sure to check the [upgrade notes](https://github.com/bolt/core/blob/main/UP
- We have also updated our Doctrine versions (data-fixtures ^2.0, dbal ^3.10). You will need to check your own entities and implementation to make sure that your code is compatible with the doctrine related changes.
- Migrations are no longer provided by Bolt. You will need to start managing the migrations yourself and take action to ensure your database remains in sync. Check the upgrade notes for the recommended approach.
- We have migrated away from annotations and fully adopted attributes. If you are relying on annotations you will need to either re-enable annotation support, or migrate them as well (the latter is the recommended approach). (bobvandevijver, [#3608](https://github.com/bolt/core/issues/3608))
- We have added the Request object to a lot of method signatures and dropped the RequestStack property (both in controller related classes). You will need to check your implementation if you were relying on those.

### Dependency updates

Expand Down
4 changes: 4 additions & 0 deletions UPGRADING.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ doctrine_migrations:

This should work on your production environment as well when you are running the `doctrine:migrations:migrate` on deployment, but as always we recommend to make a backup before trying the upgrade.

## Interface updates

- `DetailControllerInterface::record` has been updated to include the request as parameter.

## Widget updates

The following widgets were replaced:
Expand Down
2 changes: 1 addition & 1 deletion config/services_bolt.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ services:
_defaults:
autowire: true
autoconfigure: true
bind: { }
bind: { }
AcmeCorp\ReferenceExtension\:
resource: '../vendor/acmecorp/reference-extension/src/*'
exclude: '../vendor/acmecorp/reference-extension/src/{Entity,Exception}'
Expand Down
30 changes: 0 additions & 30 deletions phpstan-baseline.php
Original file line number Diff line number Diff line change
Expand Up @@ -1255,12 +1255,6 @@
'count' => 1,
'path' => __DIR__ . '/src/Controller/Backend/ContentEditController.php',
];
$ignoreErrors[] = [
'message' => '#^Parameter \\#1 \\$contentType of method Bolt\\\\Controller\\\\Backend\\\\ContentEditController\\:\\:new\\(\\) expects string, string\\|null given\\.$#',
'identifier' => 'argument.type',
'count' => 1,
'path' => __DIR__ . '/src/Controller/Backend/ContentEditController.php',
];
$ignoreErrors[] = [
'message' => '#^Parameter \\#1 \\$status of method Bolt\\\\Entity\\\\Content\\:\\:setStatus\\(\\) expects string, string\\|null given\\.$#',
'identifier' => 'argument.type',
Expand Down Expand Up @@ -1567,24 +1561,12 @@
'count' => 1,
'path' => __DIR__ . '/src/Controller/ImageController.php',
];
$ignoreErrors[] = [
'message' => '#^Cannot call method get\\(\\) on Bolt\\\\Configuration\\\\Content\\\\ContentType\\|null\\.$#',
'identifier' => 'method.nonObject',
'count' => 1,
'path' => __DIR__ . '/src/Controller/TwigAwareController.php',
];
$ignoreErrors[] = [
'message' => '#^Cannot call method setMaxPerPage\\(\\) on Bolt\\\\Entity\\\\Content\\|Pagerfanta\\\\Pagerfanta\\|null\\.$#',
'identifier' => 'method.nonObject',
'count' => 1,
'path' => __DIR__ . '/src/Controller/TwigAwareController.php',
];
$ignoreErrors[] = [
'message' => '#^Method Bolt\\\\Controller\\\\TwigAwareController\\:\\:createPager\\(\\) has no return type specified\\.$#',
'identifier' => 'missingType.return',
'count' => 1,
'path' => __DIR__ . '/src/Controller/TwigAwareController.php',
];
$ignoreErrors[] = [
'message' => '#^Method Bolt\\\\Controller\\\\TwigAwareController\\:\\:createPagerParams\\(\\) return type has no value type specified in iterable type array\\.$#',
'identifier' => 'missingType.iterableValue',
Expand Down Expand Up @@ -1627,18 +1609,6 @@
'count' => 1,
'path' => __DIR__ . '/src/Controller/TwigAwareController.php',
];
$ignoreErrors[] = [
'message' => '#^Method Bolt\\\\Controller\\\\TwigAwareController\\:\\:renderTemplate\\(\\) has parameter \\$template with no type specified\\.$#',
'identifier' => 'missingType.parameter',
'count' => 1,
'path' => __DIR__ . '/src/Controller/TwigAwareController.php',
];
$ignoreErrors[] = [
'message' => '#^Parameter \\#1 \\$contentType of method Bolt\\\\Controller\\\\TwigAwareController\\:\\:validLocaleForContentType\\(\\) expects Bolt\\\\Configuration\\\\Content\\\\ContentType, Bolt\\\\Configuration\\\\Content\\\\ContentType\\|null given\\.$#',
'identifier' => 'argument.type',
'count' => 1,
'path' => __DIR__ . '/src/Controller/TwigAwareController.php',
];
$ignoreErrors[] = [
'message' => '#^Method Bolt\\\\DataFixtures\\\\BaseFixture\\:\\:getImagesIndex\\(\\) return type with generic class Illuminate\\\\Support\\\\Collection does not specify its types\\: TKey, TValue$#',
'identifier' => 'missingType.generics',
Expand Down
4 changes: 2 additions & 2 deletions src/Controller/Backend/Async/EmbedController.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ public function __construct(RequestStack $requestStack)
}

#[Route(path: '/embed', name: 'bolt_async_embed', methods: [Request::METHOD_POST])]
public function fetchEmbed(): JsonResponse
public function fetchEmbed(Request $request): JsonResponse
{
try {
$this->validateCsrf('editrecord');
$this->validateCsrf($request, 'editrecord');
} catch (InvalidCsrfTokenException $e) {
return new JsonResponse([
'error' => [
Expand Down
4 changes: 2 additions & 2 deletions src/Controller/Backend/Async/UploadController.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public function __construct(
public function handleURLUpload(Request $request, ValidatorInterface $validator): Response
{
try {
$this->validateCsrf('upload');
$this->validateCsrf($request, 'upload');
} catch (InvalidCsrfTokenException $e) {
return new JsonResponse([
'error' => [
Expand Down Expand Up @@ -107,7 +107,7 @@ public function handleURLUpload(Request $request, ValidatorInterface $validator)
public function handleUpload(Request $request): JsonResponse
{
try {
$this->validateCsrf('upload');
$this->validateCsrf($request, 'upload');
} catch (InvalidCsrfTokenException $e) {
return new JsonResponse([
'error' => [
Expand Down
8 changes: 4 additions & 4 deletions src/Controller/Backend/BulkOperationsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ public function em(): ObjectManager
}

#[Route(path: '/bulk/status/{status}', name: 'bolt_bulk_status', methods: [Request::METHOD_POST])]
public function status(string $status): Response
public function status(Request $request, string $status): Response
{
$this->validateCsrf('batch');
$this->validateCsrf($request, 'batch');
$formData = $this->request?->request->getString('records') ?? '';
$recordIds = array_map(intval(...), explode(',', $formData));

Expand All @@ -69,9 +69,9 @@ public function status(string $status): Response
}

#[Route(path: '/bulk/delete', name: 'bolt_bulk_delete', methods: [Request::METHOD_POST])]
public function delete(): Response
public function delete(Request $request): Response
{
$this->validateCsrf('batch');
$this->validateCsrf($request, 'batch');
$formData = $this->request?->request->getString('records') ?? '';
$recordIds = array_map(intval(...), explode(',', $formData));

Expand Down
49 changes: 25 additions & 24 deletions src/Controller/Backend/ContentEditController.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
Expand Down Expand Up @@ -67,7 +68,7 @@ public function __construct(
}

#[Route(path: '/new/{contentType}', name: 'bolt_content_new', methods: [Request::METHOD_GET, Request::METHOD_POST])]
public function new(string $contentType, ?ContentValidatorInterface $contentValidator = null): Response
public function new(Request $request, string $contentType, ?ContentValidatorInterface $contentValidator = null): Response
{
$content = new Content();

Expand All @@ -82,31 +83,31 @@ public function new(string $contentType, ?ContentValidatorInterface $contentVali

$this->contentFillListener->fillContent($content);

if ($this->request?->getMethod() === 'POST') {
if ($request->getMethod() === 'POST') {
$content->setPublishedAt(null);
$content->setDepublishedAt(null);

return $this->save($content, $contentValidator);
return $this->save($request, $content, $contentValidator);
}

return $this->edit($content);
return $this->edit($request, $content);
}

#[Route(path: '/edit/{id}', name: 'bolt_content_edit', requirements: ['id' => '\d+'], methods: [Request::METHOD_GET])]
public function edit(Content $content): Response
public function edit(Request $request, Content $content): Response
{
$this->denyAccessUnlessGranted(ContentVoter::CONTENT_EDIT, $content);

$event = new ContentEvent($content);
$this->dispatcher->dispatch($event, ContentEvent::ON_EDIT);

return $this->renderEditor($content);
return $this->renderEditor($request, $content);
}

#[Route(path: '/edit/{id}', name: 'bolt_content_edit_post', requirements: ['id' => '\d+'], methods: [Request::METHOD_POST])]
public function save(?Content $originalContent = null, ?ContentValidatorInterface $contentValidator = null): Response
public function save(Request $request, ?Content $originalContent = null, ?ContentValidatorInterface $contentValidator = null): Response
{
$this->validateCsrf('editrecord');
$this->validateCsrf($request, 'editrecord');

// pre-check on original content, store properties for later comparison
if ($originalContent !== null) {
Expand Down Expand Up @@ -154,7 +155,7 @@ public function save(?Content $originalContent = null, ?ContentValidatorInterfac
if (count($constraintViolations) > 0) {
$this->addFlash('danger', 'content.validation_errors');

return $this->renderEditor($content, $constraintViolations);
return $this->renderEditor($request, $content, $constraintViolations);
}
}

Expand All @@ -173,7 +174,7 @@ public function save(?Content $originalContent = null, ?ContentValidatorInterfac

$urlParams = [
'id' => $content->getId(),
'edit_locale' => $this->getEditLocale($content) ?: null,
'edit_locale' => $this->getEditLocale($request, $content) ?: null,
];
$url = $this->urlGenerator->generate('bolt_content_edit', $urlParams);

Expand All @@ -183,7 +184,7 @@ public function save(?Content $originalContent = null, ?ContentValidatorInterfac
$locale = $originalAuthor->getLocale();

// If we're "Saving Ajaxy"
if ($this->request?->isXmlHttpRequest()) {
if ($request->isXmlHttpRequest()) {
$modified = sprintf(
'(%s: %s)',
$this->translator->trans('field.modifiedAt', [], null, $locale),
Expand Down Expand Up @@ -211,7 +212,7 @@ public function save(?Content $originalContent = null, ?ContentValidatorInterfac
}

#[Route(path: '/duplicate/{id}', name: 'bolt_content_duplicate', requirements: ['id' => '\d+'], methods: [Request::METHOD_GET])]
public function duplicate(Content $content): Response
public function duplicate(Request $request, Content $content): Response
{
$this->denyAccessUnlessGranted(ContentVoter::CONTENT_CREATE, $content);

Expand All @@ -231,29 +232,29 @@ public function duplicate(Content $content): Response
$twigvars = [
'record' => $content,
'locales' => $content->getLocales(),
'currentlocale' => $this->getEditLocale($content),
'currentlocale' => $this->getEditLocale($request, $content),
'defaultlocale' => $this->defaultLocale,
];

return $this->render('@bolt/content/edit.html.twig', $twigvars);
}

#[Route(path: '/duplicate/{id}', name: 'bolt_content_duplicate_post', requirements: ['id' => '\d+'], methods: [Request::METHOD_POST])]
public function duplicateSave(?Content $content = null): Response
public function duplicateSave(Request $request, ?Content $content = null): Response
{
$this->denyAccessUnlessGranted(ContentVoter::CONTENT_CREATE, $content);

return $this->new($content->getContentType());
return $this->new($request, $content->getContentType() ?? throw new NotFoundHttpException());
}

#[Route(path: '/status/{id}', name: 'bolt_content_status', requirements: ['id' => '\d+'], methods: [Request::METHOD_GET])]
public function status(Content $content): Response
public function status(Request $request, Content $content): Response
{
$this->validateCsrf('status');
$this->validateCsrf($request, 'status');

$this->denyAccessUnlessGranted(ContentVoter::CONTENT_CHANGE_STATUS, $content);

$content->setStatus($this->getFromRequest('status'));
$content->setStatus($this->getFromRequest($request, 'status'));

$event = new ContentEvent($content);
$this->dispatcher->dispatch($event, ContentEvent::PRE_STATUS_CHANGE);
Expand All @@ -273,9 +274,9 @@ public function status(Content $content): Response
}

#[Route(path: '/delete/{id}', name: 'bolt_content_delete', requirements: ['id' => '\d+'], methods: [Request::METHOD_GET])]
public function delete(Content $content): Response
public function delete(Request $request, Content $content): Response
{
$this->validateCsrf('delete');
$this->validateCsrf($request, 'delete');

$this->denyAccessUnlessGranted(ContentVoter::CONTENT_DELETE, $content);

Expand Down Expand Up @@ -616,9 +617,9 @@ static function (Relation $relation) use ($id): bool {
}
}

private function getEditLocale(Content $content): string
private function getEditLocale(Request $request, Content $content): string
{
$locale = $this->getFromRequest('edit_locale', '');
$locale = $this->getFromRequest($request, 'edit_locale', '');
$locales = $content->getLocales();

if ($locales->contains($locale) === false) {
Expand All @@ -637,13 +638,13 @@ private function getPostedLocale(array $post): ?string
return $post['_edit_locale'] ?? null;
}

private function renderEditor(Content $content, $errors = null): Response
private function renderEditor(Request $request, Content $content, $errors = null): Response
{
$twigvars = [
'record' => $content,
'locales' => $content->getLocales(),
'defaultlocale' => $this->defaultLocale,
'currentlocale' => $this->getEditLocale($content),
'currentlocale' => $this->getEditLocale($request, $content),
];

if ($errors) {
Expand Down
8 changes: 4 additions & 4 deletions src/Controller/Backend/DashboardController.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,17 @@
class DashboardController extends TwigAwareController implements BackendZoneInterface
{
#[Route(path: '/', name: 'bolt_dashboard', methods: [Request::METHOD_GET])]
public function index(Query $query): Response
public function index(Request $request, Query $query): Response
{
$this->denyAccessUnlessGranted('dashboard');

// TODO PERMISSIONS: implement listing that only lists content that the user is allowed to see
$amount = (int) $this->config->get('general/records_per_page', 10);
$page = (int) ($this->request?->get('page') ?? 1);
$page = (int) ($request->get('page') ?? 1);
$contentTypes = $this->config->get('contenttypes')->where('show_on_dashboard', true)->keys()->implode(',');
$filter = strip_tags((string) $this->getFromRequest('filter', ''));
$filter = strip_tags((string) $this->getFromRequest($request, 'filter', ''));

$pager = $this->createPager($query, $contentTypes, $amount, '-modifiedAt');
$pager = $this->createPager($request, $query, $contentTypes, $amount, '-modifiedAt');
$nbPages = $pager->getNbPages();

if ($page > $nbPages) {
Expand Down
Loading