Pular para o conteúdo principal

Arquitetura do SGMBlocks

Este documento descreve cada arquivo e cada pasta do módulo com foco no contrato técnico. Use-o como referência ao criar um bloco novo, trocar a factory de um formulário do core ou anexar handlers a eventos do Omeka S.


Visão geral

O SGMBlocks é um módulo padrão de Omeka S e roda sobre o Laminas Framework. O módulo é declarado por Module.php na raiz, que o Omeka carrega automaticamente ao descobrir a pasta do módulo. O ciclo de vida segue a ordem: carregamento de module.config.php, merge com a configuração do Omeka, bootstrap do ServiceManager do Laminas, disparo de Module::onBootstrap quando existe, e anexo dos listeners em Module::attachListeners. Toda a lógica do módulo entra na aplicação por esses dois ganchos.


Module.php

O arquivo Module.php estende Omeka\Module\AbstractModule e expõe dois métodos principais.

  • getConfig retorna o array de config/module.config.php. O Omeka funde esse array à sua configuração global. Toda vez que o módulo precisa expor um serviço, uma factory, um formulário ou um bloco, o registro acontece nesse arquivo.
  • attachListeners recebe o Laminas\EventManager\SharedEventManagerInterface e anexa os handlers do módulo. No SGMBlocks existem quatro anexações:
// PageCustomFieldsHandler
$sharedEventManager->attach(
\Omeka\Form\PageLayoutDataForm::class,
'form.add_elements',
[$pageHandler, 'handlePageLayoutForm']
);

// SiteCustomFieldsHandler, três eventos
$sharedEventManager->attach(\Omeka\Form\SiteSettingsForm::class, 'form.add_elements', [$siteHandler, 'handleSiteSettings']);
$sharedEventManager->attach(\Omeka\Form\SiteSettingsForm::class, 'form.add_input_filters', [$siteHandler, 'handleSiteSettingsFilters']);
$sharedEventManager->attach('Omeka\Controller\SiteAdmin\Index', 'view.layout', [$siteHandler, 'handleSiteSettingsAssets']);

O Module.php não mantém lógica de domínio. Toda lógica fica nos handlers e nas classes de bloco.


config/

module.config.php

O arquivo expõe as seguintes chaves:

ChavePropósito técnico
view_manager.template_path_stackAdiciona dirname(__DIR__) . '/view' à pilha do Laminas\View\Resolver\TemplatePathStack. O tema ainda tem prioridade.
block_layouts.factoriesAssocia cada block_layout a uma factory do ServiceManager. O Omeka usa esse mapa ao renderizar ou editar blocos.
form_elements.invokablesMapeia classes de Fieldset e Form para o FormElementManager. Permite FormElementManager::get sem factory.
form_elements.factoriesReescreve Omeka\Form\PageLayoutDataForm para a PageLayoutDataFormFactory do módulo. É a única troca de factory do core.
translator.translation_file_patternsRegistra o padrão %s.mo em language/ com type = gettext. Strings marcadas com // @translate entram no fluxo de i18n do Omeka.

module.ini

Metadados lidos pela tela de módulos do admin:

[info]
name = "SGM Blocks"
version = "1.3"
author = "Forma UFES"
configurable = false
description = "Módulo customizado para o site do SGM."
omeka_version_constraint = "^4.0.1"

A flag configurable = false significa que o módulo não expõe uma tela própria de configuração. Toda a configuração relevante acontece nos formulários do Omeka onde o módulo injeta campos.


src/

A pasta src/ segue a convenção PSR-4 com o namespace raiz SGMBlocks mapeado ao diretório. Cada subpasta tem um papel específico.

src/Form

Define os formulários Laminas dos blocos. Cada classe estende Laminas\Form\Fieldset e implementa init em vez de usar o construtor. Os nomes dos elementos seguem sempre o padrão o:block[__blockIndex__][o:data][chave] para que o Omeka substitua __blockIndex__ pelo índice real do bloco ao renderizar o formulário e para que os dados caiam corretamente em o:data.

Classes presentes:

  • AccordionBlockForm. Dois campos estáticos title e description. Itens dinâmicos são injetados pelo accordion-block-admin.js, não por esse Fieldset.
  • BannerPageForm. Sete campos: image como Omeka\Form\Element\Asset, title, description, content_align (center/left), use_overlay, show_search_bar, show_search_suggestions. Os três últimos levam a classe banner-page-center-only.
  • ButtonBlockForm. Três campos: button_text, button_url, button_style.
  • IconBlockForm. Quatro campos: icon, title, description, alignment.
  • PageDescriptionForm. Um campo description com class="block-html full wysiwyg" para ativar o CKEditor do Omeka.
  • SearchBarBlockForm. Um campo show_suggestions.
  • ThumbnailBlockForm. Um campo asset com tipo Omeka\Form\Element\Asset.

src/Site/BlockLayout

Cada classe estende Omeka\Site\BlockLayout\AbstractBlockLayout e implementa pelo menos três métodos:

  • getLabel. Retorna o rótulo traduzível exibido na lista de blocos do editor de página.
  • form. Recebe PhpRenderer $view, SiteRepresentation $site, SitePageRepresentation $page = null, SitePageBlockRepresentation $block = null. Obtém o Fieldset via $this->formElementManager->get(...), chama populateValues se $block existir, e retorna o HTML do formCollection.
  • render. Recebe PhpRenderer $view e SitePageBlockRepresentation $block. Lê os valores com $block->dataValue('chave') e delega ao partial via $view->partial('common/block-layout/nome', [...]).

Alguns blocos implementam também prepareForm para carregar CSS ou JS no admin:

  • AccordionBlock::prepareForm carrega admin.css e accordion-block-admin.js.
  • BannerPage::prepareForm carrega admin.css e banner-page-admin.js.
  • IconBlock::prepareForm carrega admin.css.
  • ThumbnailBlock::prepareForm carrega admin.css.

ButtonBlock, PageDescription e SearchBarBlock não injetam assets no admin.

src/Service/BlockLayout

Factories que o ServiceManager usa ao resolver cada block_layout. Todas implementam Laminas\ServiceManager\Factory\FactoryInterface e compartilham o mesmo padrão:

public function __invoke(ContainerInterface $services, $requestedName, array $options = null)
{
return new AccordionBlock($services->get('FormElementManager'));
}

A dependência única é o FormElementManager. Os blocos não recebem outros serviços porque obtêm a API do Omeka pelo PhpRenderer em tempo de render.

src/Service/Form

Três classes com papéis distintos:

  • PageCustomFieldsHandler. Classe pura sem dependências, instanciada diretamente em Module::attachListeners. Exporta handlePageLayoutForm, que adiciona o checkbox o:layout_data[fixed_header] ao formulário alvo do evento.
  • SiteCustomFieldsHandler. Recebe o ContainerInterface no construtor. Expõe três handlers, um por evento:
    • handleSiteSettingsOmeka\Settings\Site, normaliza o JSON salvo, registra o element_group sgmblocks e adiciona o campo hidden sgmblocks_search_suggestions.
    • handleSiteSettingsFilters adiciona um filter Callback ao input filter do sgmblocks_search_suggestions. O callback aceita array ou string JSON e retorna array.
    • handleSiteSettingsAssets verifica action === 'edit' no params fromRoute e injeta admin.css e site-settings.js no layout.
  • PageLayoutDataFormFactory. Substitui a factory padrão do PageLayoutDataForm para injetar EventManager no formulário. Sem essa troca, form.add_elements não seria disparado e o PageCustomFieldsHandler nunca rodaria.

view/

Pilha de templates

O Laminas View procura partials usando a pilha registrada em view_manager.template_path_stack. O Omeka S adiciona primeiro o view/ do tema ativo e depois o view/ de cada módulo, na ordem de registro. O partial do tema vence quando existe. Se o tema não tem o arquivo, o resolver usa o partial do módulo como fallback.

O module.config.php registra o diretório com:

'view_manager' => [
'template_path_stack' => [
dirname(__DIR__) . '/view',
]
],

Estrutura em common/block-layout

Todos os partials do módulo vivem em view/common/block-layout. Cada bloco chama seu partial com um caminho relativo à pilha. Mapeamento completo:

PartialClasse consumidoraObservação técnica
common/block-layout/accordion-block.phtmlAccordionBlockCarrega style.css, gera <section> com role="region", aria-expanded, aria-controls, e inclui script inline de toggle.
common/block-layout/banner-page.phtmlBannerPageCarrega style.css, aplica style="background-image: url(...)" e classes sgm-banner-content-{content_align}.
common/block-layout/button-block.phtmlButtonBlockRenderiza <a> com classes sgm-button-filled ou sgm-button-outline a depender do button_style.
common/block-layout/icon-block.phtmlIconBlockCarrega style.css, aplica sgm-icon-block--left ou sgm-icon-block--center.
common/block-layout/page-description.phtmlPageDescriptionImprime o HTML da descrição sem escapeHtml. Assume conteúdo confiável vindo do CKEditor.
common/block-layout/search-bar-block.phtmlSearchBarBlockResolve a URL de busca pelo siteSetting('search_type') entre site/cross-site-search e site/resource. Lê sugestões do setting.
common/block-layout/thumbnail-block/render.phtmlThumbnailBlockCarrega style.css, aplica max-width: 200px inline e renderiza <img> com alt proveniente de altText ou fallback fixo.

Override pelo tema

Para sobrescrever um partial, o tema cria o mesmo caminho relativo dentro de sua pasta view/. Exemplo: para customizar o banner, o tema cria themes/<nome>/view/common/block-layout/banner-page.phtml. O resolver encontra primeiro o do tema e ignora o do módulo. Nenhuma configuração adicional é necessária.


asset/

asset/css

  • style.css. Consumido pelo frontend. Define estilos para thumbnail, banner, acordeão e bloco de ícone. Os partials anexam o arquivo via $this->headLink()->appendStylesheet($this->assetUrl('css/style.css', 'SGMBlocks')).
  • admin.css. Consumido pelo admin. Carregado por prepareForm dos blocos que precisam de preview de imagem e pelo SiteCustomFieldsHandler::handleSiteSettingsAssets na tela de edição do site.

asset/js

  • accordion-block-admin.js. jQuery, IIFE e estado local. Escuta o evento o:block-added para inicializar blocos de acordeão novos e também inicializa no document.ready. Renderiza campos com nomes o:block[__blockIndex__][o:data][items][i][title] e [...][text], inicializa CKEditor em cada textarea com a classe wysiwyg, reindexa ao remover itens e destrói instâncias do CKEditor no unload do item.
  • site-settings.js. Manipula a lista de sugestões de busca no admin. Lê e grava o campo hidden sgmblocks-search-suggestions em JSON.

Helper assetUrl

O helper assetUrl recebe o caminho relativo e o nome do módulo. No SGMBlocks, sempre SGMBlocks. A URL final aponta para modules/SGMBlocks/asset/<caminho> servida pelo Omeka.


language/

Diretório para arquivos .po e .mo seguindo o padrão gettext. O module.config.php registra:

'translator' => [
'translation_file_patterns' => [
[
'type' => 'gettext',
'base_dir' => dirname(__DIR__) . '/language',
'pattern' => '%s.mo',
'text_domain' => null,
],
],
],

Strings marcadas com // @translate em qualquer arquivo PHP podem ser extraídas por ferramentas compatíveis e traduzidas em arquivos pt_BR.mo, en_US.mo etc. No estado atual o módulo usa apenas pt_BR implícito pelas strings em português.


Contratos importantes para lembrar

  • __blockIndex__ é um placeholder literal que o Omeka substitui pelo índice do bloco no formulário. Sempre use-o nos name dos elementos de formulário de bloco.
  • populateValues não aceita o formato de array. Sempre passe um array com chaves como o:block[__blockIndex__][o:data][campo] para preencher inputs existentes.
  • $block->dataValue('chave') é o acesso canônico a um valor de o:data. Retorna null quando ausente.
  • $view->api()->read('assets', $id) lança exceção quando o asset não existe. Todas as classes que leem assets envolvem a chamada em try catch e seguem com $asset = null.
  • $view->partial resolve templates pela pilha do template_path_stack. A ordem entre tema e módulo é determinada pelo Omeka S no bootstrap do site.