Conecte a curadoria do Acervo Vivo ao seu site WordPress. Sincronize obras, exposições e publicações de forma automatizada, mantendo total soberania sobre os seus dados e mídias.
O Acervo Vivo Sync é a ponte inteligente entre a curadoria física/catalogação da sua plataforma e o seu site público. Criado sob medida para artistas, galerias e acervos privados, o plugin recria nativamente no WordPress todas as suas obras de arte, mostras, ensaios críticos e catálogos de livros em estruturas organizadas de forma limpa, rápida e totalmente otimizada para o Google.
Diferenciais Técnicos e de Curadoria
Foco Absoluto na Obra (Sem distrações)
O site é projetado para destacar o trabalho artístico. Fichas técnicas, legendas e carrosséis adicionais de imagens são integrados de forma limpa e fluida. As listas de exposições e bibliografias nas páginas de obras são apresentadas em formato textual simples, evitando links de saída que dispersam o foco do seu visitante.
Sincronização Automática ou Em Tempo Real (Instantâneo)
Chega de esperar o dia virar para refletir uma alteração de status ou uma venda. Além da sincronização diária em segundo plano, o plugin possui integração com Webhooks, permitindo que qualquer alteração de curadoria ou disponibilidade no painel reflita no seu site WordPress em poucos segundos.
Soberania Digital (Seus dados pertencem a você)
Livre-se da dependência de plataformas exclusivas (*vendor lock-in*). Se você decidir cancelar a assinatura, o seu site WordPress permanece 100% no ar, funcional e com todos os dados técnicos e imagens intactos e locais. A plataforma funciona apenas como o canal de curadoria; o legado digital gerado é irrevogavelmente seu.
Asset Mirroring (Imagens Locais e Protegidas)
Ao invés de carregar links temporários da nuvem que expiram rapidamente, o plugin realiza o download físico das fotos principais e carrosséis de imagens diretamente para a Biblioteca de Mídia do seu próprio servidor WordPress, garantindo que as imagens nunca fiquem quebradas.
Pronto para Desenvolvedores e SEO
Com Custom Post Types nativos (`obras`, `exposicoes`, `textos`, `publicacoes`) e links amigáveis, o plugin é compatível com qualquer tema do mercado e permite que o desenvolvedor do seu site customize o layout como desejar.
Como Funciona? (3 Passos Simples)
1. Instale o Plugin: Baixe o arquivo `.zip` e faça o upload na seção “Plugins” do seu WordPress.
2. Conecte com sua API ID: Insira a chave única de conexão gerada no seu painel do Acervo Vivo na tela de configurações do plugin.
3. Force a Primeira Sincronização: Acesse as configurações do *Acervo Vivo Sync* no painel do WordPress e force a primeira sincronização de dados para carregar o seu acervo no site.
4. Crie suas Grades Visuais: Use o shortcode `[acervo_grade tipo=”obras”]` para renderizar instantaneamente uma galeria responsiva em qualquer página. Você pode definir colunas (ex: `colunas=”3″`) e o modo de imagem (ex: `imagem=”contain”` para exibir imagens cheias centralizadas sem cortes). Exemplo: `[acervo_grade tipo=”obras” serie=”Gotas” colunas=”3″ imagem=”contain”]`.
Histórico de Versões (Changelog)
* **Versão 1.1.3 (Versão Atual):**
* **Correção de Bug de Ordenação:** Correção na consulta do shortcode `[acervo_grade]`, forçando a conversão numérica (`NUMERIC`) dos índices de ordenação no banco de dados do WordPress. Isso impede que valores de ordenação de 3 dígitos (ex: `100`) sejam renderizados antes de 2 dígitos (ex: `95`) devido a comparações de strings alfabéticas.
* **Versão 1.1.2:**
* **Aprimoramento de UI de Sincronização:** Introdução do badge dinâmico de status `🟡 Sincronizando…` com uma micro-animação pulsar amarela/dourada ativa no painel administrativo do WordPress enquanto o sincronizador em segundo plano via WP-Cron estiver ativamente processando dados.
* **Versão 1.1.1:**
* **Prevenção de Travas de Concorrência:** Adicionado botão de liberação de trava de concorrência (`Manual Lock Release`) no painel de controle administrativo do plugin, além de refinamentos na idempotência de importação para evitar registros duplicados.
* **Versão 1.1.0:**
* **Orquestração Assíncrona via Webhooks:** Migração do gatilho de sincronização em tempo real (`POST /wp-json/acervo-vivo/v1/sync-trigger`) para um modelo assíncrono não-bloqueante por meio do WP-Cron, com encerramento de requisição de frontend (Abort) de 5 segundos para blindar a usabilidade da plataforma.
* **Versão 1.0.0:**
* **Lançamento Inicial:** Sincronização básica de acervos via API Headless do Acervo Vivo (`obras`, `exposicoes`, `textos`, `publicacoes`), incluindo o espelhamento automatizado de imagens (Asset Mirroring) para a biblioteca local.
—
Documentação e Suporte
Guia de Instalação Rápida
# Acervo Vivo Sync
O **Acervo Vivo Sync** é o plugin WordPress oficial para sincronizar e expor o catálogo da plataforma curatorial **Acervo Vivo** (Headless CMS/Airtable). Ele automatiza o espelhamento físico de mídias, gerencia taxonomias e expõe dados em tempo real sob medida para artistas, galerias e colecionadores.
---
## 🚀 Funcionalidades Principais
* **Sincronização em Tempo Real (Webhook):** Atualização instantânea de status de venda ou novas obras sem depender do agendamento diário. * **Renderização Dinâmica (Arquitetura Limpa):** Mantém o banco de dados limpo gravando apenas textos brutos (`post_content`), montando os layouts premium e badges em tempo de exibição via hook `the_content`. * **Segurança contra Sobrecarga (Race Conditions):** Sistema de trava de concorrência que impede "tempestades de sincronização" no servidor (retorna `409 Conflict`). * **Asset Mirroring (Download Local):** Salva as fotos na biblioteca do WordPress de forma idempotente, contornando a expiração de assinaturas CDN externas. * **Resolução de IDs Nativos:** Traduz as relações do Airtable em IDs de posts reais do WordPress (`_acervo_wp_*_ids`) para consultas rápidas no frontend.
---
## 🔌 Sincronização em Tempo Real via Webhook
O plugin expõe um endpoint REST público e seguro para receber alertas de alteração da plataforma de curadoria.
### Autenticação A chamada exige o identificador exclusivo `apiId` (o mesmo salvo nas configurações do plugin) passado de duas formas: 1. No cabeçalho da requisição HTTP: **`X-Acervo-API-ID`** 2. No payload JSON: **`apiId`**
---
## 🛠️ Exemplos de Teste do Gatilho
### Teste 1: Autenticação via Cabeçalho (cURL) Substitua `seu-site.com` e `seu-api-id` com os dados correspondentes: ```bash curl -X POST https://seu-site.com/wp-json/acervo-vivo/v1/sync-trigger \ -H "Content-Type: application/json" \ -H "X-Acervo-API-ID: seu-api-id" ```
### Teste 2: Autenticação via Corpo JSON (cURL) ```bash curl -X POST https://seu-site.com/wp-json/acervo-vivo/v1/sync-trigger \ -H "Content-Type: application/json" \ -d '{"apiId": "seu-api-id"}' ```
* **`200 OK`**: Sincronização concluída com sucesso. Exemplo de retorno: ```json { "status": "success", "message": "[Webhook] Sincronização via Webhook realizada com sucesso: 12 Obras, 2 Exposições...", "stats": { "created": 1, "updated": 5 } } ``` * **`401 Unauthorized`**: Chave `apiId` inválida, ausente ou não configurada no painel. * **`409 Conflict`**: Outra rodada de sincronização já está ativa no servidor (trava de segurança temporária ativa). * **`500 Internal Server Error`**: Instabilidade na conexão com o servidor do Acervo Vivo ou falha fatal na ingestão de dados.
---
## 🎨 Exibição de Grades (Shortcode)
Insira o shortcode em qualquer editor de blocos, Elementor ou construtor clássico: * Listagem de Obras: `[acervo_grade tipo="obras"]` * Filtrar Obras por Linguagem: `[acervo_grade tipo="obras" linguagem="Colagem"]` * Filtrar Obras por Série: `[acervo_grade tipo="obras" serie="Paisagens"]` * Limitar quantidade: `[acervo_grade tipo="obras" limite="6"]`
Documentação Técnica para Desenvolvedores
# Acervo Vivo Sync — WordPress Plugin (Developer Documentation)
Este documento descreve as especificações técnicas, decisões arquiteturais e funcionamento do plugin **Acervo Vivo Sync** (v1.0.0).
---
## 1. Visão Geral O **Acervo Vivo Sync** é um plugin WordPress projetado para conectar a plataforma Headless CMS do Acervo Vivo ao site WordPress do cliente (artista, galeria ou colecionador).
O plugin lê a API pública em formato JSON, cria e sincroniza automaticamente os Custom Post Types (CPTs) locais correspondentes às entidades de curadoria e resolve a exibição de imagens usando a estratégia **Asset Mirroring (Download Físico)** com **Idempotência estrita**, **Garbage Collection (limpeza de disco)** e **Trava de Concorrência**.
## 3. Custom Post Types (CPTs) e SEO Slugs O plugin registra 4 tipos de posts customizados com suporte ao editor de blocos (Gutenberg) e URLs amigáveis:
| Nome do Post Type | Rótulo (PT-BR) | Slug Público de URL | Ícone Admin (Dashicons) | |---|---|---|---| | `acervo_artwork` | Obras | `/obras/` | `dashicons-art` | | `acervo_event` | Exposições | `/exposicoes/` | `dashicons-tickets-alt` | | `acervo_text` | Textos | `/textos/` | `dashicons-editor-paragraph` | | `acervo_publication` | Publicações | `/publicacoes/` | `dashicons-book-alt` |
---
## 4. Integração de API e Auto-paginação
### Endpoint Seguro (Sandbox) * **URL**: `GET https://app.acervovivo.com/api/v1/public/sandbox?apiId={apiId}` * **Segurança**: Isolamento de inquilinos (Tenant Isolation) por meio do `apiId` (ID da Conta). A validação de permissão de uso da API (`UsaAPI`) é resolvida no servidor do Acervo Vivo, retornando `HTTP 403` se inativo.
### Loop de Paginação (PHP client) A API do Airtable possui um limite fixo de 100 registros por requisição. A função do plugin `acervo_vivo_sync_fetch_data( $api_id )` executa de forma segura um loop `do/while`: 1. Dispara a chamada inicial GET para o endpoint. 2. Mescla os arrays de registros recebidos (`artworks`, `events`, `texts`, `publications`, `series`). 3. Se a resposta contiver um campo `offset` não vazio, concatena o parâmetro `&offset={offset}` na URL e realiza uma nova requisição. 4. Repete até que o campo `offset` esteja ausente da resposta ou atinja o teto de segurança de **50 páginas** (prevenção contra loops infinitos).
---
## 5. Motor de Ingestão e Persistência (Upsert)
O método `process_sync_data()` processa a carga de dados. Para evitar a duplicação de dados, o plugin compara os registros contra as chaves determinísticas abaixo:
### Chaves de Unicidade * **Obras (`acervo_artwork`)**: Busca no banco de dados pela chave de metadados privados `_acervo_container_code` correspondente ao campo `containerCode` vindo da API Sandbox (Ex: `BOT-012`). O slug (`post_name`) do post no WP é gerado a partir desta chave. * **Exposições, Textos e Publicações**: Utilizam o campo de metadados privado `_acervo_id` contendo o ID exclusivo de registro da API (RecordID do Airtable).
### Lógica do Método `upsert_post()` - **Se o registro já existe**: Recupera o ID do post cadastrado, preserva suas propriedades e faz o `wp_update_post` para atualizar dados de título, descrição e metadados. Incrementa o contador `updated`. - **Se o registro é novo**: Executa `wp_insert_post` para registrar no banco. Incrementa o contador `created`. - **Fechamento de Comentários e Pings (Segurança e SEO)**: Toda inserção ou atualização no método força os campos `'comment_status' => 'closed'` e `'ping_status' => 'closed'`, desativando globalmente discussões e trackbacks nos 4 CPTs do Acervo Vivo.
### Trava de Concorrência (Race Condition Prevention) Durante disparos manuais e automáticos simultâneos (como o gatilho automático do WP-Cron concorrendo com um clique de administrador), poderia ocorrer inserções e downloads duplicados. O plugin implementa um sistema de travamento baseado em **Transients do WordPress**: 1. No início do fluxo de entrada (`handle_manual_sync` e `run_cron_sync`), o plugin verifica o transient `acervo_vivo_sync_lock`. 2. Se o transient existir, a execução é interrompida imediatamente para evitar concorrência. 3. Se livre, o transient `acervo_vivo_sync_lock` é gravado com validade de 10 minutos. 4. Toda a lógica de ingestão roda sob um bloco estruturado `try / finally`, o que garante a deleção automática do transient no final da execução, mesmo em caso de erro fatal ou timeout. 5. **Mecanismo de Desbloqueio de Segurança**: Se o servidor sofrer queda ou encerrar o script abruptamente antes de limpar a trava, o administrador visualizará um alerta destacado no painel configurações e poderá clicar no botão **Forçar Destravamento** para limpar o transient e liberar a fila manualmente.
---
## 6. Layouts Ricos de Conteúdo (Renderização Dinâmica via the_content)
Para manter o banco de dados limpo e permitir que editores alterem o texto bruto sem danificar a estrutura visual ou perder os dados nas sincronizações, o plugin adota uma estratégia de **Renderização Dinâmica** no frontend através do hook `the_content`:
1. **Persistência de Dados Limpos**: Durante a ingestão, o plugin grava apenas o texto bruto original no campo `post_content` (Conceito para Obras, Descrição para Exposições/Publicações, Conteúdo para Textos). 2. **Hook de Interceptação**: Registra o filtro `add_filter( 'the_content', array( $this, 'render_dynamic_cpt_content' ) )`. 3. **Condições de Execução**: O filtro é ativado apenas em visualizações singulares (`is_singular()`), dentro da query principal do loop (`in_the_loop() && is_main_query()`) para os 4 Custom Post Types. 4. **Layout Premium**: O HTML estruturado com grades, fichas técnicas e badges, juntamente com o bloco de estilos CSS encapsulado, é injetado em tempo de execução.
### Obras (`acervo_artwork`) O layout compila dinamicamente: * **Conceito/Texto Curatorial**: Renderizado no topo (retornado do editor de post bruto). * **Legenda**: Bloco formatado em itálico contendo a legenda descritiva fluida (técnica, dimensões, ano, crédito). Se vazia no Airtable, o plugin constrói um fallback a partir das chaves individuais. * **Badges de Situação, Localização e Catálogo**: Badges de estilo premium e responsivo (ex: `🟢 Disponível`, `📍 Ateliê do Artista`, `Acervo: 00054` - este último com preenchimento para 5 dígitos). * **Processo Criativo**: Seção dedicada com detalhes de processo (`_acervo_artwork_process`). * **Histórico de Exposições e Referências Bibliográficas**: Listas estruturadas em texto simples. **Importante**: Conforme diretriz de curadoria e UX da plataforma, essas listas não contêm hiperlinks externos ou de saída para evitar a dispersão do foco do visitante da página da obra. * **Galeria de Imagens (Carrossel)**: Executa o shortcode nativo `
` com os IDs dos anexos correspondentes recuperados de `_acervo_carousel_hashes`.
### Exposições (`acervo_event`) O layout compila dinamicamente: * **Capa / Imagem**: Injeta a imagem destacada no topo. * **Descrição do Evento**: Conteúdo descritivo bruto do post. * **Caixa de Informações**: Bloco destacado contendo Data de Abertura, Local, Cidade, Tipo de Evento e Linha de Currículo pronta para cópia. * **Clipping**: Clipping de imprensa e críticas logo abaixo.
### Textos (`acervo_text`) O layout compila dinamicamente: * **Corpo do Texto**: O texto na íntegra. * **Metadados**: Detalhes de Autoria, Data de Publicação e Tipo de Texto. * **Resumo de IA**: Um bloco destacado (`.acervo-text-summary`) com borda lateral contrastante contendo o resumo executivo gerado por Inteligência Artificial.
### Publicações (`acervo_publication`) O layout compila dinamicamente: * **Descrição**: Resumo editorial da obra escrita. * **Ficha Técnica**: Bloco contendo Autoria, Editora, Ano de Lançamento, ISBN, Edição, Páginas, Tipo e Linha no Currículo. * **Clipping**: Trechos de resenhas críticas. * **Botão de Acesso**: Botão responsivo (`.acervo-btn-cta`) direcionando para o link externo.
---
## 7. Mapeamento de Metadados Privados e Vínculos Relacionais
Todos os metadados específicos da curadoria são persistidos com o prefixo sublinhado `_acervo_` (ocultos na interface padrão de campos personalizados do WP Admin):
### Metadados de Obras (`acervo_artwork`) * `_acervo_id`: RecordID do Airtable. * `_acervo_container_code`: Código do container. * `_acervo_image_url`: URL temporária de referência da API. * `_acervo_downloaded_image_url`: URL de controle para o Sideload idempotente. * `_acervo_artwork_year`: Ano da obra. * `_acervo_artwork_medium`: Técnica. * `_acervo_artwork_dimensions`: Dimensões. * `_acervo_artwork_carousel_images`: Array serializado contendo links do carrossel. * `_acervo_artwork_series`: Séries às quais a obra pertence. * `_acervo_artwork_photo_credit`: Crédito fotográfico. * `_acervo_artwork_pure_code`: Código puro numérico. * `_acervo_artwork_caption`: Legenda formatada. * `_acervo_artwork_curatorial_order`: Ordem soberana curatorial. * `_acervo_artwork_process`: Detalhes de processo criativo. * `_acervo_artwork_status`: Situação do trabalho. * `_acervo_artwork_location`: Localização da obra. * `_acervo_artwork_exhibition_lines`: Array de strings contendo linhas de exposições. * `_acervo_artwork_publication_lines`: Array de strings contendo referências de publicações. * `_acervo_carousel_hashes`: Dicionário serializado `[url_path => attachment_id]` para controle de idempotência do carrossel.
### Metadados de Exposições (`acervo_event`) * `_acervo_id`: RecordID do Airtable. * `_acervo_image_url`: URL de capa temporária da API. * `_acervo_downloaded_image_url`: URL de controle para o Sideload. * `_acervo_event_year`: Ano. * `_acervo_event_type`: Tipo do evento. * `_acervo_event_venue`: Local. * `_acervo_event_city`: Cidade. * `_acervo_event_link`: Link externo. * `_acervo_event_opening_date`: Data de abertura. * `_acervo_event_curriculum_line`: Linha pronta para CV. * `_acervo_event_clipping`: Textos de clipping de imprensa. * `_acervo_event_artwork_ids`: RecordIDs (Airtable) de obras relacionadas. * `_acervo_event_text_ids`: RecordIDs (Airtable) de textos relacionados. * **`_acervo_wp_artwork_ids`**: Array de IDs inteiros de posts reais (CPT `acervo_artwork`) do WordPress correspondentes às obras vinculadas. * **`_acervo_wp_text_ids`**: Array de IDs inteiros de posts reais (CPT `acervo_text`) do WordPress correspondentes aos textos vinculados.
### Metadados de Textos (`acervo_text`) * `_acervo_id`: RecordID do Airtable. * `_acervo_text_type`: Tipo do texto. * `_acervo_text_author`: Autor. * `_acervo_text_date`: Data de publicação. * `_acervo_text_ai_summary`: Resumo gerado por Inteligência Artificial. * `_acervo_text_artwork_ids`: RecordIDs (Airtable) de obras relacionadas. * `_acervo_text_event_ids`: RecordIDs (Airtable) de exposições relacionadas. * **`_acervo_wp_artwork_ids`**: Array de IDs inteiros de posts reais (CPT `acervo_artwork`) do WordPress correspondentes às obras vinculadas. * **`_acervo_wp_event_ids`**: Array de IDs inteiros de posts reais (CPT `acervo_event`) do WordPress correspondentes às exposições vinculadas.
### Metadados de Publicações (`acervo_publication`) * `_acervo_id`: RecordID do Airtable. * `_acervo_image_url`: URL de capa temporária da API. * `_acervo_downloaded_image_url`: URL de controle para o Sideload. * `_acervo_publication_type`: Tipo de publicação. * `_acervo_publication_publisher`: Editora. * `_acervo_publication_year`: Ano de publicação. * `_acervo_publication_isbn`: Código ISBN. * `_acervo_publication_edition`: Edição. * `_acervo_publication_authorship`: Autoria/Créditos. * `_acervo_publication_pages`: Quantidade de páginas. * `_acervo_publication_external_link`: Link de destino externo (compras/visualização). * `_acervo_publication_clipping`: Textos de críticas/resenhas. * `_acervo_publication_release_date`: Data de lançamento. * `_acervo_publication_curriculum_line`: Linha pronta para CV. * `_acervo_publication_artwork_ids`: RecordIDs (Airtable) de obras relacionadas. * `_acervo_publication_text_ids`: RecordIDs (Airtable) de textos relacionados. * `_acervo_publication_event_ids`: RecordIDs (Airtable) de exposições relacionadas. * **`_acervo_wp_artwork_ids`**: Array de IDs inteiros de posts reais (CPT `acervo_artwork`) do WordPress correspondentes às obras vinculadas. * **`_acervo_wp_text_ids`**: Array de IDs inteiros de posts reais (CPT `acervo_text`) do WordPress correspondentes aos textos vinculados. * **`_acervo_wp_event_ids`**: Array de IDs inteiros de posts reais (CPT `acervo_event`) do WordPress correspondentes às exposições vinculadas.
### Resolução Automática de Relacionamentos (Sob o Capô) Ao final de cada execução de sincronização (`process_sync_data`), o resolvedor interno `resolve_relational_ids()` mapeia todos os RecordIDs de todos os posts cadastrados no banco de dados e traduz as chaves estrangeiras em arrays de Post IDs inteiros nativos do WordPress (como listados acima com o prefixo `_acervo_wp_`). Isso permite que desenvolvedores de temas façam queries relacionais extremamente eficientes usando instâncias de `WP_Query` com o parâmetro `post__in`: ```php $obras_vinculadas = get_post_meta( get_the_ID(), '_acervo_wp_artwork_ids', true ); if ( ! empty( $obras_vinculadas ) ) { $artworks_query = new WP_Query( array( 'post_type' => 'acervo_artwork', 'post__in' => $obras_vinculadas, 'posts_per_page' => -1 ) ); // Loop de renderização das obras no template da exposição... } ```
---
## 8. O Motor de Mídia (Asset Mirroring Idempotente)
Para contornar o encurtamento do tempo de vida das assinaturas temporárias da CDN do Airtable (que expiram em cerca de 2 horas), o plugin executa um fluxo robusto de download físico local.
### Nível 1: Importação de Dependências em Background No helper privado `sideload_image_to_post()`, importamos dinamicamente as classes de mídia do core administrativo do WordPress (`wp-admin/includes/image.php`, `wp-admin/includes/file.php`, `wp-admin/includes/media.php`). Isso garante que os downloads físicos não falhem quando disparados via WP-Cron ou acessos públicos (onde o ambiente administrativo não está carregado por padrão).
### Nível 2: Idempotência e Restabelecimento de Miniatura Como os parâmetros de consulta (`?auth=...&expires=...`) das URLs do Airtable rotacionam e mudam constantemente a cada requisição da API Sandbox, a comparação direta das URLs brutas falharia e causaria downloads repetidos desnecessários a cada sincronização. 1. Para resolver isso, o plugin extrai apenas o caminho de arquivo estático da URL remota utilizando: ```php $existing_path = wp_parse_url( $existing_downloaded_url, PHP_URL_PATH ); $new_path = wp_parse_url( $api_image_url, PHP_URL_PATH ); ``` 2. O download físico só é iniciado se o caminho estático (nome do arquivo e estrutura de diretório original) for modificado, ignorando tokens de expiração. 3. **Restabelecimento Inteligente de Mídias Deletadas**: Além do caminho, o plugin verifica se o ID de anexo correspondente cadastrado em `_thumbnail_id` de fato existe fisicamente no WordPress (através de `get_post()`). Se o usuário tiver excluído a imagem manualmente da biblioteca de mídias, o plugin detecta a ausência e executa o rebaixamento automático para recuperar o campo "Imagem destacada".
### Nível 3: Coletor de Lixo Completo (Garbage Collection de Disco) Para blindar o banco de dados contra mídias duplicadas e órfãs geradas no processo de atualização de imagens: 1. Quando uma alteração no caminho estático de imagem é detectada, o plugin realiza uma busca completa por todos os anexos vinculados àquele post específico usando: ```php $attachments = get_posts( array( 'post_type' => 'attachment', 'posts_per_page' => -1, 'post_parent' => $post_id, 'fields' => 'ids', ) ); ``` 2. O plugin percorre cada ID de anexo correspondente e executa `wp_delete_attachment( $att_id, true )`. O parâmetro `true` força a **remoção física definitiva dos arquivos no disco** do servidor e a limpeza dos registros no banco. 3. Isso garante o descarte total da imagem antiga antes de iniciar a gravação do novo arquivo.
### Nível 4: Mecanismo de Download de Baixo Nível e Contingência SSL Para vencer limitações de conexões e certificados SSL incompatíveis em ambientes de hospedagem compartilhada (como a Hostinger): 1. O plugin primeiro executa o download usando a função nativa `download_url( $url )`. 2. Se houver falha de handshake SSL (retornando um `WP_Error`), o plugin ativa uma contingência em tempo de execução: - Aplica dinamicamente um filtro temporário no hook `http_request_args` desativando a verificação de SSL: ```php add_filter( 'http_request_args', function( $args ) { $args['sslverify'] = false; return $args; } ); ``` - Tenta o download do arquivo temporário novamente. - Remove o filtro imediatamente após a tentativa para restaurar a segurança global das requisições externas do WordPress. 3. Sanitiza o nome do arquivo extraído antes de passá-lo para a biblioteca local de mídia com `media_handle_sideload()`.
### Nível 5: Idempotência e Sideloading do Carrossel de Imagens Adicionais Para mídias acessórias associadas às obras (`carouselImages`), o plugin implementa um motor isolado de controle baseado em hashes associativos: 1. **Dicionário de Controle**: O metadado privado `_acervo_carousel_hashes` guarda um array mapeando o caminho estático da URL da foto remota (`url_path`) para o ID do respectivo anexo local (`attachment_id`). 2. **Reutilização Inteligente**: Se o caminho estático já estiver cadastrado e a imagem ainda existir na biblioteca do WordPress, o download físico é pulado. 3. **Coletor de Lixo do Carrossel (Garbage Collection)**: Qualquer anexo antigo cujos caminhos estáticos originais não constem mais na carga de dados atual da API do Airtable é removido de forma física permanente no disco do servidor com `wp_delete_attachment( $id, true )`. 4. **Acoplamento no Conteúdo**: Ao final da importação de cada lote, o shortcode `` é gerado dinamicamente e injetado ao final do `post_content` do WordPress.
---
## 9. Sincronização Diária (WP-Cron)
Na ativação do plugin, é agendado um cron diário chamado `acervo_vivo_daily_sync_cron` que roda em segundo plano consumindo a função isolada de busca de dados e executando o motor de upsert. O agendamento é removido no gancho de desativação do plugin para evitar poluição no banco de dados WordPress.
---
## 10. Painel de Controle, Identidade Visual e Diagnóstico
O painel administrativo do plugin (`Opções > Acervo Vivo Sync`) foi redesenhado para refletir a identidade visual do **Acervo Vivo**, contando com: * **Logo Vetorial**: Exibição dinâmica do logo colorido oficial (`assets/ACVV-icon-colorido.svg`) usando `plugins_url()`. * **Painel Escuro Premium**: Visual estilizado e responsivo que separa configurações de conexão de badges de status em tempo real. * **Tabela de Diagnóstico de Mídias**: Exibição detalhada de erros físicos de download (mensagens de erro do servidor, URLs problemáticas e CPTs correspondentes) com limite de histórico persistido no banco de dados. * **Histórico de Execuções (Logs de Importação)**: Uma tabela persistida no banco de dados (`acervo_vivo_sync_history` limitado a 20 registros) que acompanha todas as tentativas e resultados de sincronização (Manual e Cron). A tabela registra o timestamp exato, a origem do gatilho, status de sucesso/erro e a mensagem detalhada do progresso. * **Alerta e Botão de Forçar Destravamento**: Um aviso proativo amarelo exibido caso o transient `acervo_vivo_sync_lock` esteja ativo no banco de dados, permitindo destravar a sincronização manualmente em caso de timeouts ou travamento do servidor.
---
## 11. Hierarquia de Templates e Customização no Tema (Front-end)
Para permitir customização visual total do front-end por parte do desenvolvedor do site, o plugin tira proveito da Hierarquia de Templates nativa do WordPress. Como as entidades foram registradas como Custom Post Types (CPTs), o programador do tema pode criar templates sob medida no diretório do tema ativo:
* **Obras (`acervo_artwork`)**: * `archive-acervo_artwork.php`: Customiza a página de listagem geral (arquivo/grade) das obras. * `single-acervo_artwork.php`: Customiza o layout da página de leitura/visualização detalhada de uma única obra. * **Exposições (`acervo_event`)**: * `archive-acervo_event.php`: Template de arquivo geral das exposições. * `single-acervo_event.php`: Template do post individual de uma exposição. * **Textos (`acervo_text`)**: * `archive-acervo_text.php`: Template de arquivo de listagem de textos. * `single-acervo_text.php`: Template do post individual de um texto. * **Publicações (`acervo_publication`)**: * `archive-acervo_publication.php`: Template de arquivo de listagem de publicações. * `single-acervo_publication.php`: Template do post individual de uma publicação.
Se esses arquivos específicos não forem criados no tema, o WordPress utilizará automaticamente o plano B do core (`archive.php` e `single.php` ou `index.php`), garantindo compatibilidade retroativa e facilidade de integração.
---
## 12. Sincronização em Tempo Real (REST API Webhook)
Para viabilizar atualizações instantâneas no WordPress e eliminar a latência de 24 horas do WP-Cron em operações comerciais ou de catálogo, o plugin disponibiliza um endpoint público seguro para integração com o back-end curatorial (Airtable / API Headless).
### Autenticação (Gatekeeper) Para autenticar a chamada, a requisição deve prover o identificador de conexão `apiId` de duas formas: 1. No cabeçalho da requisição HTTP: **`X-Acervo-API-ID`** (ex: `X-Acervo-API-ID: sandbox-id-123`). 2. No corpo da requisição JSON (payload): ```json { "apiId": "sandbox-id-123" } ``` O valor fornecido é comparado em tempo de execução com a chave armazenada na opção do WordPress `acervo_vivo_api_id`. Em caso de ausência ou divergência, o endpoint retorna erro HTTP `401 Unauthorized`.
### Proteção Contra Tempestades de Webhooks (Race Conditions) Se o curador realizar dezenas de atualizações consecutivas no Airtable, a plataforma pode enviar múltiplas requisições POST consecutivas ao WordPress. Para evitar sobrecarga no servidor do cliente e duplicação de processos: 1. O endpoint tenta obter a trava de sincronização (`acquire_sync_lock()`). 2. Caso o primeiro processo ainda esteja em execução no servidor local, todas as demais requisições concorrentes serão rejeitadas imediatamente com erro HTTP `409 Conflict`. 3. Isso garante que apenas um fluxo de importação e sideload de mídias seja executado por vez, preservando a CPU e banda da hospedagem.
### Logs e Histórico Toda execução acionada via Webhook é registrada na tabela `acervo_vivo_sync_history` com o gatilho `'webhook'`. Isso possibilita ao administrador rastrear a origem exata da sincronização no painel administrativo do plugin.
### Códigos de Resposta HTTP * `200 OK`: Sincronização executada com sucesso. Retorna estatísticas de posts criados e atualizados. * `401 Unauthorized`: O `apiId` não foi enviado ou é inválido. * `409 Conflict`: Uma sincronização já está em execução. O webhook foi rejeitado para evitar conflitos de lock. * `500 Internal Server Error`: Erro de transporte HTTP ou falha na carga de dados da API.