1. Go to this page and download the library: Download klehm/content-blocks library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
use ContentBlocks\Security\AccessCheckerInterface;
use ContentBlocks\Entity\ContentArea;
final class PageAccessChecker implements AccessCheckerInterface
{
public function canEdit(ContentArea $contentArea): bool
{
// Check that the current user owns the Page linked to this ContentArea
}
public function canView(ContentArea $contentArea): bool
{
return true;
}
}
use ContentBlocks\Entity\ContentArea;
use ContentBlocks\Preview\ContentAreaUrlResolverInterface;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
final class PageContentAreaUrlResolver implements ContentAreaUrlResolverInterface
{
public function __construct(
private readonly EntityManagerInterface $em,
private readonly UrlGeneratorInterface $urls,
) {}
public function resolve(ContentArea $area): string
{
$page = $this->em->getRepository(Page::class)->findOneBy(['contentArea' => $area]);
if (!$page) {
// Fallback while the parent entity is being created and is not yet linked
return $this->urls->generate('app_home');
}
return $this->urls->generate('app_page_show', ['id' => $page->getId()]);
}
}
use App\Entity\Page;
use ContentBlocks\Entity\ContentArea;
use ContentBlocks\Replace\ContentAreaProviderInterface;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\QueryBuilder;
final class PageContentAreaProvider implements ContentAreaProviderInterface
{
public function __construct(private readonly EntityManagerInterface $em) {}
public function createQueryBuilder(?string $filter): QueryBuilder
{
// Join through the host's owning entity (Page) so the picker can
// search on title + return only areas that have a real Page parent.
$qb = $this->em->createQueryBuilder()
->select('a')
->from(ContentArea::class, 'a')
->innerJoin(Page::class, 'p', 'WITH', 'p.contentArea = a');
if ($filter !== null && $filter !== '') {
$qb->andWhere('p.title LIKE :q')->setParameter('q', '%' . $filter . '%');
}
return $qb;
}
public function getLabel(ContentArea $area): string
{
$page = $this->em->getRepository(Page::class)->findOneBy(['contentArea' => $area]);
if (!$page) {
return '#' . $area->getId();
}
$when = $area->getUpdatedAt()?->format('Y-m-d') ?? '—';
return sprintf('%s — %s', $page->getTitle(), $when);
}
}
use ContentBlocks\Form\Type\Styling\StylingType;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
final class BrandPaletteExtension extends AbstractTypeExtension
{
public static function getExtendedTypes(): iterable
{
return [StylingType::class];
}
public function buildForm(FormBuilderInterface $builder, array $options): void
{
// Re-adding an existing field overrides it — here we replace the
// raw HTML5 ColorType with a curated brand palette.
$builder->add('backgroundColor', ChoiceType::class, [
'
use ContentBlocks\Section\SectionSettingsDefaultsProviderInterface;
final class AppSectionDefaults implements SectionSettingsDefaultsProviderInterface
{
public function getDefaults(): array
{
return [
// Top-level section setting.
'maxWidth' => 1400,
// Nested under the Styling sub-form (deep-merged).
'styling' => [
'backgroundColor' => '#f7f7f7',
],
];
}
}
use ContentBlocks\Security\AccessCheckerInterface;
use ContentBlocks\Entity\ContentArea;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
final class PageAccessChecker implements AccessCheckerInterface
{
public function __construct(
private readonly TokenStorageInterface $tokens,
private readonly RequestStack $requests,
) {}
public function canEdit(ContentArea $contentArea): bool
{
return $this->isAdmin() && $this->ownsArea($contentArea);
}
public function canView(ContentArea $contentArea): bool { return true; }
private function isAdmin(): bool
{
// 1) Standard path: a token is in the current firewall's storage.
$token = $this->tokens->getToken();
if ($token && \in_array('ROLE_ADMIN', $token->getRoleNames(), true)) {
return true;
}
// 2) Cross-firewall fallback: the iframe runs under the public
// firewall, so the admin token isn't visible via $tokens. Read
// the serialized admin token from the session directly. The key
// is `_security_<context_or_firewall_name>` — `_security_admin`
// when `context: admin` or the firewall name is `admin`.
$request = $this->requests->getMainRequest();
if (!$request || !$request->hasSession()) {
return false;
}
$serialized = $request->getSession()->get('_security_admin');
if (!\is_string($serialized)) {
return false;
}
$adminToken = unserialize($serialized);
return $adminToken instanceof TokenInterface
&& \in_array('ROLE_ADMIN', $adminToken->getRoleNames(), true);
}
private function ownsArea(ContentArea $area): bool
{
// your app's ownership check
}
}