Pular para o conteúdo principal

Descrição e thumbnail

Dois blocos cobrem os metadados visuais de uma página: page-description e thumbnail-block. Eles existem em paralelo e não têm dependência entre si, mas costumam aparecer juntos em páginas institucionais quando o tema precisa de resumo mais imagem de capa.


SGM: Descrição da Página

Como foi construído

Identificação

ItemValor
ClasseSGMBlocks\Site\BlockLayout\PageDescription
block_layoutpage-description
FormSGMBlocks\Form\PageDescriptionForm
FactorySGMBlocks\Service\BlockLayout\PageDescriptionFactory
Partialcommon/block-layout/page-description

Formulário

O PageDescriptionForm estende Laminas\Form\Fieldset e define um único elemento:

$this->add([
'name' => 'o:block[__blockIndex__][o:data][description]',
'type' => Element\Textarea::class,
'options' => ['label' => 'SGM: Descrição da Página'],
'attributes' => ['class' => 'block-html full wysiwyg'],
]);

A classe CSS block-html full wysiwyg é a chave para o Omeka inicializar o CKEditor sobre a textarea. block-html delimita o contexto, full libera toolbar completa, e wysiwyg é o gatilho do JS do Omeka.

Classe do bloco

PageDescription::form chama populateValues com a chave o:block[__blockIndex__][o:data][description] quando há $block. PageDescription::render passa description ao partial:

return $view->partial('common/block-layout/page-description', [
'description' => $block->dataValue('description'),
]);

Partial

<?php if (!empty($description)): ?>
<div class="page-description-block">
<?= $description ?>
</div>
<?php endif; ?>

Note a ausência de escape no <?= $description ?>. O partial assume conteúdo confiável vindo do CKEditor. Qualquer decisão de sanitizar deve acontecer antes de salvar, seja em form.add_input_filters, seja em um sanitizador explícito.

Persistência

Chave em o:dataTipoObservação
descriptionstring HTMLConteúdo bruto do CKEditor, não escapado.

SGM: Thumbnail da Página

Como foi construído

Identificação

ItemValor
ClasseSGMBlocks\Site\BlockLayout\ThumbnailBlock
block_layoutthumbnail-block
FormSGMBlocks\Form\ThumbnailBlockForm
FactorySGMBlocks\Service\BlockLayout\ThumbnailBlockFactory
Partialcommon/block-layout/thumbnail-block/render

Formulário

$this->add([
'name' => 'o:block[__blockIndex__][o:data][asset]',
'type' => OmekaElement\Asset::class,
'options' => ['label' => 'Imagem de Capa'],
]);

O tipo Omeka\Form\Element\Asset renderiza o seletor nativo de assets do Omeka, com botões para escolher asset existente ou fazer upload.

Classe do bloco

ThumbnailBlock::prepareForm anexa admin.css para ajustar o preview do asset no editor:

public function prepareForm(PhpRenderer $view)
{
$view->headLink()->appendStylesheet($view->assetUrl('css/admin.css', 'SGMBlocks'));
}

ThumbnailBlock::render lê o id do asset e resolve o AssetRepresentation com try catch:

$assetId = $block->dataValue('asset');
$asset = null;
if ($assetId) {
try {
$asset = $view->api()->read('assets', $assetId)->getContent();
} catch (\Exception $e) {
// asset apagado ou inacessível
}
}
return $view->partial('common/block-layout/thumbnail-block/render', ['asset' => $asset]);

Partial

<?php
$this->headLink()->appendStylesheet($this->assetUrl('css/style.css', 'SGMBlocks'));
?>

<?php if ($asset): ?>
<div class="page-thumbnail-block">
<img class="page-thumbnail-image"
style="display: block; max-width: 200px; width: auto; height: auto;"
src="<?= $asset->assetUrl() ?>"
alt="<?= $this->escapeHtmlAttr($asset->altText() ?: 'SGM: Thumbnail da Página') ?>" />
</div>
<?php endif; ?>

O style inline aplica max-width: 200px, o que limita o tamanho do thumbnail independente do CSS do tema. O alt usa altText do asset quando disponível e cai no texto fixo caso contrário.

Persistência

Chave em o:dataTipoObservação
assetinteiro id de assetResolvido via api read assets.

Diferenças de escape entre os dois partials

page-description.phtml imprime HTML cru. thumbnail-block/render.phtml escapa alt com escapeHtmlAttr e confia em $asset->assetUrl() como URL segura. Essa assimetria é intencional: a descrição precisa preservar marcações do CKEditor; o alt é texto simples.


Como usar como desenvolvedor

Sobrescrever o partial no tema

Copie o arquivo do módulo para o tema mantendo o caminho:

  • themes/<tema>/view/common/block-layout/page-description.phtml
  • themes/<tema>/view/common/block-layout/thumbnail-block/render.phtml

O resolver do Laminas encontra o do tema primeiro e ignora o do módulo. Nada precisa ser registrado.

Ajustar o tamanho do thumbnail

O max-width: 200px está inline no partial. Para mudar, edite o partial do módulo ou escreva a versão própria no tema. Ajuste também asset/css/style.css para acompanhar.

Trocar a política de escape da descrição

Se precisar sanitizar, adicione um input filter no formulário. Exemplo:

// Em um handler novo, anexado a form.add_input_filters no SiteSettingsForm do bloco
$inputFilter->add([
'name' => 'o:block[__blockIndex__][o:data][description]',
'filters' => [['name' => 'HTMLPurifier']],
]);

Alternativamente, troque <?= $description ?> por <?= $this->escapeHtml($description) ?> no partial. A troca quebra marcações ricas, então use com cuidado.

Acessar os blocos de dentro de um template

Dada uma $page, itere os blocos e filtre pelo layout:

$page = $this->page;

foreach ($page->blocks() as $block) {
if ($block->layout() === 'thumbnail-block') {
$assetId = $block->dataValue('asset');
if ($assetId) {
$asset = $this->api()->read('assets', $assetId)->getContent();
echo '<img src="' . $this->escapeHtmlAttr($asset->assetUrl()) . '" alt="' . $this->escapeHtmlAttr($asset->altText() ?: 'Thumbnail') . '" />';
}
}
}

Renderizar o bloco diretamente em outro template

Use o helper blockLayout do Omeka:

foreach ($page->blocks() as $block) {
if ($block->layout() === 'thumbnail-block') {
$blockLayout = $this->blockLayout($block->layout());
echo $blockLayout->render($this, $block);
}
}

Obter apenas a URL do thumbnail em meta tags

function getPageThumbnail($page, $api)
{
foreach ($page->blocks() as $block) {
if ($block->layout() === 'thumbnail-block') {
$assetId = $block->dataValue('asset');
if ($assetId) {
try {
return $api->read('assets', $assetId)->getContent()->assetUrl();
} catch (\Exception $e) {
return null;
}
}
}
}
return null;
}

$thumbnailUrl = getPageThumbnail($this->page, $this->api());
if ($thumbnailUrl) {
echo '<meta property="og:image" content="' . $this->escapeHtmlAttr($thumbnailUrl) . '" />';
}

Verificar se a página tem thumbnail

function hasThumbnail($page)
{
foreach ($page->blocks() as $block) {
if ($block->layout() === 'thumbnail-block' && $block->dataValue('asset')) {
return true;
}
}
return false;
}

Acessar via API

$page = $api->read('site_pages', $pageId)->getContent();
foreach ($page->blocks() as $block) {
if ($block->layout() === 'thumbnail-block') {
$assetId = $block->dataValue('asset');
// fazer algo com $assetId
}
}

Observações

  • O id do bloco é thumbnail-block. Sempre compare com $block->layout().
  • A descrição não tem validação nem sanitização. Qualquer HTML enviado pelo editor entra direto no DOM do site.
  • assetUrl em AssetRepresentation é uma URL absoluta resolvida pelo storage configurado no Omeka. Não é preciso passar pelo assetUrl helper.
  • Se o asset foi removido, api read assets lança exceção. O bloco captura e segue com $asset = null, então o partial não renderiza nada.

Exemplo de página com thumbnail e descrição