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 que é o Acervo Vivo Sync?

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.

### Rota do Webhook
* **URL**: `POST /wp-json/acervo-vivo/v1/sync-trigger`
* **Content-Type**: `application/json`

### 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"}'
```

### Teste 3: Utilizando PowerShell
```powershell
$headers = @{
"Content-Type" = "application/json"
"X-Acervo-API-ID" = "seu-api-id"
}
Invoke-RestMethod -Uri "https://seu-site.com/wp-json/acervo-vivo/v1/sync-trigger" -Method Post -Headers $headers
```

---

## 📊 Respostas da API (HTTP Status Codes)

* **`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**.

---

## 2. Especificações Arquiteturais

```
┌────────────────────────────────────────────────────────┐
│ ACERVO VIVO │
│ (Headless API) │
└──────────────────────────┬─────────────────────────────┘

│ (GET /sandbox?apiId=...)

┌────────────────────────────────────────────────────────┐
│ acervo_vivo_sync_fetch_data │
│ (Loop Auto-pagination do/while) │
└──────────────────────────┬─────────────────────────────┘

▼ (Consolidated JSON)
┌────────────────────────────────────────────────────────┐
│ process_sync_data │
│ [CONCURRENCY LOCK: Transient Check] │
└──────┬───────────────────┬───────────────────┬─────────┘
│ │ │
▼ (Artwork) ▼ (Event) ▼ (Text/Pub)
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ CPT: Artwork │ │ CPT: Event │ │ CPT: Text/Pub│
│ Slug: obras │ │Slug:exposicoes│ │ Slug: textos │
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘
│ │ │
└─────────┬─────────┴───────────────────┘

▼ (Stores CDN Url in '_acervo_image_url')
┌────────────────────────────────────────────────────────┐
│ Asset Mirroring Image Engine │
│ - Compares url path (ignores rotating signature) │
│ - If path changed: │
│ - Queries & purges previous attachments (GC) │
│ - Low-level download_url (with SSL verify bypass) │
│ - Saves new attachment and links '_thumbnail_id' │
└────────────────────────────────────────────────────────┘
```

---

## 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).

### Endpoint
* **URL**: `POST /wp-json/acervo-vivo/v1/sync-trigger`
* **Content-Type**: `application/json`

### 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.