feat: sync all plugins, skills, agents updates

New plugins: core-tools
New skills: auto-expense, ticket-triage, design, security-check,
  aiktop-tasks, daily-digest, imap-triage, index-update, mindmap,
  notebooklm, proc-creator, tasks-overview, validate-component,
  perfex-module, report, calendar-manager
New agents: design-critic, design-generator, design-lead,
  design-prompt-architect, design-researcher, compliance-auditor,
  metabase-analyst, gitea-integration-specialist
Updated: all plugin configs, knowledge datasets, existing skills

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-05 17:16:15 +00:00
parent f2b5171ea2
commit 9404af7ac9
184 changed files with 20865 additions and 1993 deletions

View File

@@ -0,0 +1,668 @@
# JetEngine — Automação Headless e Integração com Agentes IA
Arquitectura técnica para manipular o JetEngine sem wp-admin: via PHP interno, REST API, SQL e n8n.
---
## 1. Estrutura de Dados — Base de Dados
### A. Custom Post Types (CPT) — Estrutura Standard WP
```sql
-- Dados em wp_posts
SELECT * FROM wp_posts WHERE post_type = 'meu_cpt' AND post_status = 'publish';
-- Meta fields em wp_postmeta
SELECT meta_key, meta_value FROM wp_postmeta WHERE post_id = 123;
```
**Limitação:** Queries complexas com múltiplos meta fields são lentas (JOIN por cada meta key).
### B. Custom Content Types (CCT) — Tabelas SQL Próprias
Cada CCT tem tabela dedicada: `wp_jet_cct_{slug_do_cct}`
```sql
-- Exemplo: CCT "faturas" → tabela wp_jet_cct_faturas
-- Colunas são os campos definidos no JetEngine
-- Ler todos os itens activos
SELECT * FROM wp_jet_cct_faturas WHERE cct_status = 'active';
-- Filtrar por campo
SELECT * FROM wp_jet_cct_clientes
WHERE cidade = 'Lisboa'
AND cct_status = 'publish'
ORDER BY _created_at DESC
LIMIT 20;
-- Campos especiais em todos os CCT:
-- _ID → ID numérico único
-- cct_status → publish / draft
-- cct_author_id → ID do utilizador criador
-- _created_at → Unix timestamp de criação
-- _modified → Unix timestamp de modificação
```
**Vantagem:** SQL directo é seguro e extremamente rápido. Ideal para leitura em dashboards e n8n.
### C. Relations — Tabelas wp_jet_rel_default e _meta
O JetEngine NÃO guarda relações em `wp_postmeta`. Tem tabela própria.
```sql
-- wp_jet_rel_default: relações básicas
-- parent_object_id | child_object_id | rel_id (hash/slug da relação)
-- Ler filhos de um pai
SELECT child_object_id
FROM wp_jet_rel_default
WHERE parent_object_id = 123
AND rel_id = 'relacao_clientes_projetos';
-- Ler pai a partir de filho
SELECT parent_object_id
FROM wp_jet_rel_default
WHERE child_object_id = 456
AND rel_id = 'relacao_clientes_projetos';
-- Contar relações
SELECT parent_object_id, COUNT(*) as total
FROM wp_jet_rel_default
WHERE rel_id = 'relacao_empresa_colaboradores'
GROUP BY parent_object_id;
```
**wp_jet_rel_default_meta** — metadados específicos de uma relação (ex: papel do actor num filme):
```sql
-- Exemplo: condição de uso de um veículo numa relação utilizador↔veículo
SELECT meta_key, meta_value
FROM wp_jet_rel_default_meta
WHERE rel_id = 8
AND parent_object_id = 45
AND child_object_id = 1020;
-- UPSERT massivo para sincronizações ETL (com integridade referencial)
START TRANSACTION;
INSERT INTO wp_jet_rel_default (rel_id, parent_object_id, child_object_id)
VALUES (8, 45, 1020), (8, 45, 1021), (8, 92, 1055)
ON DUPLICATE KEY UPDATE parent_object_id = VALUES(parent_object_id);
INSERT INTO wp_jet_rel_default_meta (rel_id, parent_object_id, child_object_id, meta_key, meta_value)
VALUES (8, 45, 1020, 'condicao_uso', 'Alugado')
ON DUPLICATE KEY UPDATE meta_value = VALUES(meta_value);
COMMIT;
-- OBRIGATÓRIO após SQL directo: chamar wp_cache_flush() + jet_smart_filters()->indexer->index_all()
```
**Nota:** Em relações críticas com muito tráfego, o JetEngine pode isolar por relação em tabela própria (`wp_jet_rel_81_meta`), reduzindo table locks.
**Guardrail:** NUNCA inserir directamente via SQL para criar relações em ambientes activos. Usar API PHP (ver secção 2). SQL directo só para sincronizações ETL massivas (centenas de milhares de linhas).
### D. Custom Meta Storage para CPT (Tabela Dedicada)
Alternativa ao `wp_postmeta` para CPTs com muitos campos e consultas complexas.
```
JetEngine > Post Types > Edit > Custom Meta Storage: Activar
→ Cria tabela dedicada: wp_{cpt_slug}_meta
(ex: wp_imoveis_meta com colunas tipadas por campo)
```
```sql
-- Com Custom Meta Storage activo, cada campo é uma coluna
SELECT preco, area_m2, tipologia, localizacao
FROM wp_imoveis_meta
WHERE preco BETWEEN 150000 AND 350000
AND tipologia IN ('T2', 'T3')
ORDER BY preco ASC;
-- Joins com wp_posts permanecem necessários para title/date
SELECT p.ID, p.post_title, m.preco, m.area_m2
FROM wp_posts p
JOIN wp_imoveis_meta m ON m.post_id = p.ID
WHERE p.post_status = 'publish'
AND m.preco < 300000;
```
**Quando usar:** CPTs com >10 campos numéricos/date, queries com múltiplos filtros simultâneos, alternativa a CCT quando se precisa de taxonomias e comentários nativos WP.
### E. Estrutura na wp_options
```sql
-- Configurações de CPTs
SELECT option_value FROM wp_options WHERE option_name = 'jet_engine_cpt';
-- Taxonomias
SELECT option_value FROM wp_options WHERE option_name = 'jet_engine_tax';
-- Relações
SELECT option_value FROM wp_options WHERE option_name = 'jet_engine_relations';
-- Meta Boxes
SELECT option_value FROM wp_options WHERE option_name = 'jet_engine_meta_boxes';
```
**Aviso:** São PHP serialized arrays. NUNCA editar via SQL string replace — vai corromper. Usar sempre `unserialize → edit → serialize` em PHP.
---
## 2. PHP — API Interna JetEngine
### Criar/Actualizar Item CCT
```php
<?php
// Usar CCT Data Handler (mantém hooks, validações e cache)
// Mais seguro que SQL directo para escrita
$cct_slug = 'faturas';
$item_data = [
'titulo' => 'Fatura #1024',
'valor' => 500.00,
'status' => 'pendente',
'cliente' => 'Empresa X Lda',
// Para update, incluir _ID existente:
// '_ID' => 15
];
// Aceder ao módulo CCT
if (function_exists('jet_engine')) {
$cct_module = jet_engine()->modules->get_module('custom-content-types');
if ($cct_module) {
$manager = $cct_module->instance->manager;
$type = $manager->get_content_type_instance($cct_slug);
if ($type) {
$result = $type->get_handler_instance()->update_item($item_data);
// $result = ID do item criado, ou false em caso de erro
}
}
}
```
### Gerir Relações Programaticamente
```php
<?php
// Método 1: process_relation (simples, para connect/disconnect)
// IMPORTANTE: Executar após o hook 'init' com prioridade > 12
if (class_exists('\Jet_Engine\Relations\Manager')) {
$relations_manager = \Jet_Engine\Relations\Manager::get_instance();
$relations_manager->process_relation(
$rel_id, // ex: 'relacao_empresa_colaboradores'
$parent_id,
$child_id,
true // true = conectar, false = desconectar
);
}
// Método 2: API completa (update com contexto — mais robusto)
function ia_conectar_com_contexto($cliente_id, $documento_id) {
$rel_id_numerico = 15; // ID numérico da relação (ver JetEngine > Relations)
$relation = jet_engine()->relations->get_active_relations($rel_id_numerico);
if ($relation) {
$relation->set_update_context('parent'); // perspectiva: 'parent' ou 'child'
$relation->update($cliente_id, $documento_id);
// Limpar cache do Query Builder após actualizar relações
\Jet_Engine\Query_Builder\Manager::instance()->listings_cache->clear();
}
}
```
### Criar/Actualizar CPT Post
```php
<?php
// CPTs usam funções standard do WordPress
$post_data = [
'post_title' => 'Novo Projecto',
'post_content' => '',
'post_status' => 'publish',
'post_type' => 'projecto',
];
$post_id = wp_insert_post($post_data);
if (!is_wp_error($post_id)) {
update_post_meta($post_id, 'cliente_id', 45);
update_post_meta($post_id, 'valor_contrato', 5000);
update_post_meta($post_id, 'data_inicio', '2026-03-01');
}
```
### Manipular Estrutura (Onboarding Programático)
```php
<?php
// Adicionar novo CPT à configuração do JetEngine
// CUIDADO: substitui configurações existentes se não fizer merge
$current_cpts = get_option('jet_engine_cpt', []);
$new_cpt = [
'slug' => 'imovel',
'singular_name' => 'Imóvel',
'name' => 'Imóveis',
'show_in_rest' => true,
'supports' => ['title', 'editor', 'thumbnail', 'custom-fields'],
// ... outras configurações
];
$current_cpts[] = $new_cpt;
update_option('jet_engine_cpt', $current_cpts);
// Obrigatório: regenerar permalinks após alterar CPTs
flush_rewrite_rules();
```
### Registar Meta Box Programaticamente (Hook)
```php
<?php
// Hook: jet-engine/meta-boxes/register-instances
// Ideal para plugins de agência (controlo por código, imutável pelo cliente)
add_action('jet-engine/meta-boxes/register-instances', function($meta_manager) {
$meta_fields = [
[
'title' => 'ID Remoto de Sincronização',
'name' => '_ia_sync_id',
'object_type' => 'field',
'type' => 'text',
'width' => '50%',
],
[
'title' => 'Score de Auditoria',
'name' => '_score_auditoria',
'object_type' => 'field',
'type' => 'number',
'width' => '50%',
],
];
$post_type = 'documento_legal';
if (!class_exists('Jet_Engine_CPT_Meta')) {
require $meta_manager->component_path('post.php');
}
$meta_manager->store_fields($post_type, $meta_fields, 'post_type');
new Jet_Engine_CPT_Meta($post_type, $meta_fields, 'Controlo IA', 'normal', 'high', [
'object_type' => 'post',
'allowed_post_type' => [$post_type],
'name' => 'Controlo de Metadados IA',
]);
});
```
### WP-CLI Batch Import para CCT
```php
<?php
// Ficheiro: import_cct_data.php
// Executar: wp eval-file import_cct_data.php
if (!class_exists('\Jet_Engine\Modules\Custom_Content_Types\Module')) {
WP_CLI::error('Módulo CCT não está activo.');
}
$cct_slug = 'veiculos';
$manager = \Jet_Engine\Modules\Custom_Content_Types\Module::instance()->manager;
$content_type = $manager->get_content_types($cct_slug);
if (!$content_type) {
WP_CLI::error("CCT '{$cct_slug}' não encontrado.");
}
$registos = json_decode(file_get_contents('/tmp/veiculos.json'), true);
$sucesso = 0;
foreach ($registos as $dados) {
// db->insert() respeita sanitizações e integridade do CCT
if ($content_type->db->insert($dados)) {
$sucesso++;
}
// Libertar memória em loops grandes
if ($sucesso % 500 === 0) {
wp_cache_flush();
}
}
wp_cache_flush();
WP_CLI::success("Importação concluída. Registos criados: {$sucesso}");
```
```bash
# Execução no servidor CWP:
PHP="/opt/alt/php-fpm83/usr/bin/php"
WP="$PHP /usr/local/bin/wp --allow-root --path=/home/USER/public_html"
$WP eval-file /tmp/import_cct_data.php
```
---
## 3. REST API JetEngine
### Endpoints
| Tipo | Endpoint | Método |
|------|----------|--------|
| CPT | `/wp-json/wp/v2/{cpt_slug}` | GET / POST / PUT / DELETE |
| CCT | `/wp-json/jet-cct/{cct_slug}` | GET / POST / PUT / DELETE |
| Relação | `/wp-json/jet-rel/{relation_id}` | GET / POST |
**Pré-requisito:** Activar REST API nas configurações do CPT/CCT/Relação em JetEngine.
### Autenticação
```bash
# Application Passwords (WP 5.6+) — recomendado para n8n
# Criar em: WP Admin > Users > Edit > Application Passwords
# Header de autenticação
Authorization: Basic $(echo -n 'username:app-password' | base64)
```
### Criar/Ler Item CCT via REST
```bash
# Criar registo no CCT "clientes"
curl -X POST https://site.pt/wp-json/jet-cct/clientes \
-H "Authorization: Basic dXNlcjpwYXNz..." \
-H "Content-Type: application/json" \
-d '{
"nome_cliente": "Empresa X Lda",
"email": "contacto@empresax.com",
"nif": "123456789",
"cct_status": "publish"
}'
# Resposta: { "_ID": 42, ... }
# Pesquisa com filtros — parâmetros CCT REST API
# _limit, _offset: paginação
# _cct_search: pesquisa de texto
# _cct_search_by: campo específico
curl "https://site.pt/wp-json/jet-cct/clientes?_limit=20&_offset=0&_cct_search=Lisboa&_cct_search_by=cidade"
# Header de resposta inclui: Jet-Query-Total (total de registos para paginação)
```
### Actualizar Relação via REST API
```bash
# POST para /wp-json/jet-rel/{relation_id}
# store_items_type: "update" (aditivo) | "replace" (destrutivo!) | "disconnect" (remover)
curl -X POST https://site.pt/wp-json/jet-rel/22 \
-H "Authorization: Basic dXNlcjpwYXNz..." \
-H "Content-Type: application/json" \
-d '{
"parent_id": 4500,
"child_id": 1020,
"context": "child",
"store_items_type": "update",
"meta": {
"etapa_validacao": "Aprovado",
"score_auditoria": 88.5,
"data_processamento": "2026-02-18T08:30"
}
}'
```
**Atenção `store_items_type`:**
- `update` — adiciona à relação existente (safe)
- `replace`**DESTRÓI todas as relações existentes** e substitui (perigoso!)
- `disconnect` — remove a relação específica
### Criar Post (CPT) via REST
```bash
curl -X POST https://site.pt/wp-json/wp/v2/projecto \
-H "Authorization: Basic dXNlcjpwYXNz..." \
-H "Content-Type: application/json" \
-d '{
"title": "Projecto Website ABC",
"status": "publish",
"meta": {
"cliente_id": 45,
"valor_contrato": 5000
}
}'
```
### Campos Repeater via REST
```json
{
"galeria_servicos": [
{ "titulo": "Serviço A", "descricao": "Descrição A" },
{ "titulo": "Serviço B", "descricao": "Descrição B" }
]
}
```
---
## 4. Integração n8n
### Leitura (MySQL Node — mais rápido)
```javascript
// n8n MySQL Node — ler CCT directamente da BD
// Muito mais rápido que REST API para leitura em massa
// Query example:
SELECT
_ID,
nome_cliente,
email,
cidade,
cct_status,
_created_at
FROM wp_jet_cct_clientes
WHERE cct_status = 'publish'
ORDER BY _created_at DESC
LIMIT 100;
```
**Vantagem:** 100x mais rápido que HTTP Request ao WP REST API.
### Escrita (HTTP Request Node — via REST)
```javascript
// n8n HTTP Request Node
// Method: POST
// URL: https://site.pt/wp-json/jet-cct/faturas
// Authentication: Basic Auth
// Body (JSON):
{
"numero": "{{ $json.fatura_numero }}",
"cliente_id": "{{ $json.cliente_id }}",
"valor": "{{ $json.total }}",
"cct_status": "publish"
}
```
**Usar REST para escrita** (garante hooks JetEngine e validações).
### Webhooks via JetFormBuilder
```
JetFormBuilder pode disparar Webhook para n8n:
1. Criar formulário no JetFormBuilder
2. Post-submit Action: Webhook
3. URL: https://n8n.descomplicar.pt/webhook/jetengine
4. Formulários silenciosos: receber POST externos que passam pelas validações JetEngine
```
---
## 5. Tabela de Métodos
| Método | Complexidade | Performance | Risco | Caso de Uso |
|--------|-------------|-------------|-------|-------------|
| REST API | Baixa | Média | Baixo | Inserir dados externos via n8n |
| PHP (Classes Internas) | Média | Alta | Baixo | Plugins custom, scripts de migração |
| SQL Directo (CCT) | Alta | Muito Alta | Médio | Bulk updates, leitura analytics |
| SQL Directo (Meta) | Alta | Baixa | **Alto** | **Evitar** — corrupção de arrays |
| WP-CLI Custom | Média | Alta | Baixo | Manutenção, cron jobs |
---
## 6. Guardrails Críticos
### Cache JetEngine
```bash
# Após alterar estruturas via PHP/SQL na wp_options,
# forçar regeneração de cache:
PHP="/opt/alt/php-fpm83/usr/bin/php"
WP="$PHP /usr/local/bin/wp --allow-root --path=/home/USER/public_html"
$WP cache flush
$WP rewrite flush
$WP elementor flush-css --regenerate
```
**Cache do Query Builder** — obrigatório após alterações SQL directas:
```php
<?php
// Limpar cache de listagens após SQL bulk update
\Jet_Engine\Query_Builder\Manager::instance()->listings_cache->clear();
// Recalcular Indexer do JetSmartFilters (se filtros activos)
if (function_exists('jet_smart_filters')) {
jet_smart_filters()->indexer->index_all();
}
// Cache do WordPress
wp_cache_flush();
```
**Regra crítica:** Alterações SQL directas em `wp_jet_cct_*` ou `wp_jet_rel_default` NÃO invalidam automaticamente o Object Cache nem os transients do Query Builder. O site apresentará dados obsoletos até ao flush.
### NUNCA Editar Campos Serializados via SQL String Replace
```sql
-- ERRADO — corrompe o array PHP serializado
UPDATE wp_options
SET option_value = REPLACE(option_value, 'Imóveis', 'Propriedades')
WHERE option_name = 'jet_engine_cpt';
-- CORRECTO — usar PHP
$cpts = get_option('jet_engine_cpt');
// Fazer alterações via PHP
update_option('jet_engine_cpt', $cpts);
```
### IDs de Relações em Migrações
```
Problema: Ao migrar dados entre ambientes, IDs podem mudar → relações quebram
Solução: Usar Slugs como referência no código; converter para ID antes de gravar
Usar: get_posts(['name' => 'slug-do-post', 'post_type' => 'meu_cpt'])
```
### Repeater Fields via SQL
```
Repeater fields são PHP serialize() ou JSON dependendo da configuração.
NUNCA fazer string replace em SQL.
Usar sempre: unserialize() → editar array → serialize() → update_post_meta()
```
---
## 7. MU-Plugin: Wrapper de Endpoints Custom
Para expor endpoints seguros ao n8n sem passar pela REST API standard:
```php
<?php
/**
* Plugin Name: JetEngine AI Agent Endpoints
* Description: Endpoints seguros para criação de CCTs e Relações via n8n
* Must Use: sim — colocar em /wp-content/mu-plugins/
*/
add_action('rest_api_init', function() {
// Endpoint para criar item CCT
register_rest_route('jet-agent/v1', '/cct/(?P<slug>[a-z_]+)', [
'methods' => 'POST',
'callback' => 'jet_agent_create_cct_item',
'permission_callback' => function() {
return current_user_can('edit_posts');
},
'args' => [
'slug' => ['required' => true, 'sanitize_callback' => 'sanitize_key'],
]
]);
// Endpoint para gerir relações
register_rest_route('jet-agent/v1', '/relation', [
'methods' => 'POST',
'callback' => 'jet_agent_manage_relation',
'permission_callback' => function() {
return current_user_can('edit_posts');
}
]);
});
function jet_agent_create_cct_item(WP_REST_Request $request) {
$slug = $request->get_param('slug');
$data = $request->get_json_params();
// Sanitizar dados
$data = array_map('sanitize_text_field', $data);
// Usar handler interno do JetEngine
if (!function_exists('jet_engine')) {
return new WP_Error('jet_engine_missing', 'JetEngine não activo', ['status' => 500]);
}
$cct_module = jet_engine()->modules->get_module('custom-content-types');
$type = $cct_module->instance->manager->get_content_type_instance($slug);
if (!$type) {
return new WP_Error('cct_not_found', "CCT '{$slug}' não encontrado", ['status' => 404]);
}
$result = $type->get_handler_instance()->update_item($data);
return rest_ensure_response([
'success' => (bool)$result,
'id' => $result,
]);
}
function jet_agent_manage_relation(WP_REST_Request $request) {
$params = $request->get_json_params();
$rel_id = sanitize_text_field($params['rel_id'] ?? '');
$parent_id = absint($params['parent_id'] ?? 0);
$child_id = absint($params['child_id'] ?? 0);
$connect = (bool)($params['connect'] ?? true);
if (!$rel_id || !$parent_id || !$child_id) {
return new WP_Error('missing_params', 'rel_id, parent_id e child_id são obrigatórios', ['status' => 400]);
}
if (class_exists('\Jet_Engine\Relations\Manager')) {
\Jet_Engine\Relations\Manager::get_instance()->process_relation(
$rel_id, $parent_id, $child_id, $connect
);
return rest_ensure_response(['success' => true]);
}
return new WP_Error('relations_missing', 'Módulo Relations não disponível', ['status' => 500]);
}
```
---
*JetEngine Automation | Descomplicar® | 18-02-2026*

View File

@@ -0,0 +1,271 @@
# JetEngine — Dynamic Content, Macros e Profile Builder
## Dynamic Tags no Elementor
### Categorias de Dynamic Tags JetEngine
| Categoria | Tags Disponíveis |
|-----------|-----------------|
| **Post** | Post Title, Post URL, Post Date, Post Author, Featured Image, Post Terms |
| **Meta** | Meta Field (qualquer campo JetEngine) |
| **User** | User Name, User Email, User Role, User Meta |
| **Relations** | Relation Field (conteúdo relacionado) |
| **Options** | Option Field (JetEngine Global Options Pages) |
| **Macros** | JetEngine Macros (ver secção abaixo) |
| **Query** | Query Variable (parâmetros URL) |
### Usar Meta Field Tag
```
Elemento Elementor > Campo de texto > ícone Dynamic Tags
→ JetEngine > Meta Field
→ Seleccionar meta key name
→ Field Type: auto / text / image / url / ...
Para imagens (campo tipo media):
→ Field Type: Image
→ Image Size: thumbnail / medium / large / full
Para URLs em botões:
→ Link > Dynamic Tag > Meta Field > Field Type: URL
```
### Tags em CSS Custom (background, cores)
```
Elemento > Style > Background > Dynamic Tag
→ Meta Field → campo cor HEX
→ Útil para cards coloridos por categoria
```
---
## Macros
### Macros Built-in
```
%current_user_id% # ID do utilizador logado
%current_user_name% # Nome do utilizador
%current_user_email% # Email do utilizador
%current_post_id% # ID do post actual
%current_post_type% # Post type do post actual
%queried_object_id% # ID em arquivo/single
%queried_object_slug% # Slug em arquivo/single
%request_field|param_name% # Parâmetro URL: ?param_name=valor
%current_meta|field_name% # Meta field do post actual
%user_meta|field_name% # Meta field do utilizador logado
%current_taxonomy|tax_name% # Taxonomy do post actual
%current_timestamp% # Unix timestamp actual
%site_url% # URL do site
```
### Usar Macros em Query Builder
```
Exemplo: Listar imóveis favoritos do utilizador
Meta Query:
├── Field: favorito_de (campo que guarda array de IDs de utilizadores)
├── Operator: LIKE
└── Value: %current_user_id%
Exemplo: Listar items criados pelo utilizador
Meta Query:
├── Field: cct_author_id (campo automático do CCT)
├── Operator: =
└── Value: %current_user_id%
```
### Macros em Textos
```
Texto: "Bem-vindo, %current_user_name%!"
→ Em runtime: "Bem-vindo, João Silva!"
Em Heading Widget: activar Dynamic Tag > JetEngine Macros
→ Escrever a macro no campo Macro Value
```
### Criar Macro Custom (PHP)
```php
<?php
// Registar macro custom
add_filter('jet-engine/listings/macros-list', function($macros) {
$macros[] = [
'id' => 'minha_macro',
'name' => 'Minha Macro Custom',
'args' => [], // parâmetros opcionais
];
return $macros;
});
add_filter('jet-engine/listings/macros-result', function($result, $macro, $args) {
if ($macro === 'minha_macro') {
// Lógica da macro
$result = 'valor calculado';
}
return $result;
}, 10, 3);
```
---
## Conditional Fields (Campos Condicionais)
### Em Meta Boxes
```
Campo B só aparece quando Campo A = "sim"
Meta Box > Campo B > Conditions:
├── Field: nome_campo_a
├── Operator: =
└── Value: sim
```
### Em Listing Templates (Visibility)
```
Elemento Elementor > Advanced > JetEngine Conditions:
├── Condition: Meta Field | Equals | "featured"
└── Effect: Show / Hide
```
---
## Global Options Pages
Para configurações globais do site (telefone, email, horários, redes sociais).
### Criar Options Page
```
JetEngine > Options Pages > Add New
├── Page Title: Informações Globais
├── Slug: informacoes-globais
└── Campos: Meta Box associado
```
### Usar em Elementor
```
Dynamic Tag > JetEngine > Option
→ Seleccionar Options Page
→ Seleccionar campo
Exemplo: footer com telefone dinâmico
→ Options Page: Informações Globais > campo: telefone
→ Editar telefone em 1 lugar, actualiza em todo o site
```
---
## Profile Builder
Sistema de áreas de conta para utilizadores logados.
### Estrutura
```
JetEngine > Profile Builder
├── Pages (áreas da conta):
│ ├── Dashboard (página principal)
│ ├── Submissões (posts do utilizador)
│ ├── Favoritos
│ ├── Configurações (editar perfil)
│ └── [custom pages]
├── Access Rules:
│ ├── Login Required: YES
│ ├── User Roles: subscriber / author / ...
│ └── Redirect não-logados: página de login
└── Profile Menu Widget (Elementor)
```
### Criar Página de Perfil
```
1. JetEngine > Profile Builder > Add Page
Nome: Dashboard | Slug: dashboard
Template: Criar com Elementor
2. No Template, usar Dynamic Tags:
→ User Name, User Email, User Avatar
→ Query Builder filtrado por %current_user_id%
3. Adicionar Profile Menu Widget ao header/sidebar
→ Mostra links para cada página do perfil
→ Altera links consoante se está logado ou não
```
### Submissão de Conteúdo por Utilizadores
```
JetFormBuilder + Profile Builder:
1. Criar formulário com acção "Create Post" (CPT)
2. Acção: Set Author = Current User
3. Colocar formulário na página de perfil
4. Query Builder na página de listagem: autor = current_user_id
```
### Editar Perfil com JetFormBuilder
```php
// JetFormBuilder Form:
// Acção: Update User
// Campos mapeados:
// ├── first_name → user first_name
// ├── last_name → user last_name
// ├── email → user email
// └── bio_custom → user_meta: bio_custom
// Pre-fill automático: activar "Use data from:" → Current User
```
---
## Conditional Content por Utilizador
### Mostrar Conteúdo Só a Logados
```
Elementor > Advanced > JetEngine Conditions:
Condition: Is User Logged In | Equals | True
Effect: Show (esconde elemento para não logados)
```
### Por Role
```
Condition: User Role | Contains | administrator
Effect: Show (só admins vêem este bloco)
```
### Por Meta do Utilizador
```
Condition: User Meta | Field: plano_subscricao | Equals | premium
Effect: Show (só utilizadores premium vêem este conteúdo)
```
---
## Repeater em Frontend
### Listar Items de um Repeater
```
Meta Box com campo Repeater "galeria_destaques":
├── Sub-campo: imagem (media)
├── Sub-campo: titulo (text)
└── Sub-campo: link (url)
Em Listing Template:
Adicionar widget "Dynamic Repeater"
→ Source: Meta Field → galeria_destaques
→ Criar template para cada item do repeater
```
---
*JetEngine Dynamic Content | Descomplicar® | 18-02-2026*

View File

@@ -0,0 +1,225 @@
# JetEngine — Query Builder e Listings
## Query Builder Avançado
### Tipos de Query e Configuração
#### Query Posts
```
Campos principais:
├── Post Type: CPT ou post/page
├── Post Status: publish / draft / any
├── Posts per page: N (ou -1 para todos)
├── Order: ASC / DESC
├── Order By: date / title / menu_order / meta_value / meta_value_num / rand
│ └── Se meta_value: seleccionar meta key
├── Meta Query (múltiplas condições):
│ ├── Field: meta key name
│ ├── Type: CHAR / NUMERIC / DATE
│ ├── Operator: = / != / > / < / BETWEEN / LIKE / IN / NOT IN / EXISTS
│ └── Value: valor ou macro (%current_user_id%)
└── Tax Query:
├── Taxonomy: seleccionar taxonomy
├── Operator: IN / NOT IN / AND
└── Terms: valores ou relação dinâmica
```
#### Query CCT
```
Campos principais:
├── Content Type: seleccionar CCT
├── Items per page: N
├── Order By: qualquer coluna do CCT
├── Filters (equivalente a Meta Query):
│ ├── Field: coluna do CCT
│ ├── Operator: = / != / > / LIKE / IN
│ └── Value: valor ou macro
└── Show items only for current user: activar para perfis
```
#### Query Relação
```
Objetivo: listar items relacionados ao post/item actual
├── Relation: seleccionar relação criada no JetEngine
├── Get children of: post actual / ID específico / macro
└── Post Type: tipo dos items relacionados
```
### Meta Query com Macros
```
Exemplo: listar projectos do utilizador logado
Meta Query:
├── Field: cliente_user_id
├── Operator: =
└── Value: %current_user_id%
Exemplo: listar items de hoje
Meta Query:
├── Field: data_evento (timestamp)
├── Type: NUMERIC
├── Operator: >=
└── Value: %current_timestamp%
```
### Combinar Múltiplas Condições
```
Relation: AND (todas as condições verdadeiras)
OR (pelo menos uma verdadeira)
Exemplo: imóveis em Lisboa com preço < 300k
Tax Query 1: Localização = Lisboa
Meta Query 1: Preço < 300000
Relation: AND
```
---
## Listing Grid — Configuração Detalhada
### Criar Listing Template
```
JetEngine > Listings > Add New
Nome: [CPT]-listing-card
Listing From: Posts / Terms / Users / CCT
Post Type: [seleccionar]
Editar com Elementor:
├── Usar Dynamic Tags em todos os campos
├── Post Title Widget → ou Text Widget com dynamic tag
├── Meta Field → adicionar Dynamic Tag > JetEngine > Meta Field
├── Post Featured Image → ou Image Widget com dynamic tag
└── Button → URL com dynamic tag > Post URL
```
### Listing Grid Widget — Parâmetros
```
Listing Item: [seleccionar template]
Use Custom Query: YES → seleccionar Query Builder
Items per page: 12 (padrão)
Not Found Text: "Sem resultados"
Loader: activar para UX com filtros
Columns: 1/2/3/4 (responsivo)
Equal Columns Height: YES para cards uniformes
```
### Paginação
```
Listing Grid > Pagination:
├── Type: Default WP / Load More / Infinity Scroll
├── Load More Button Text: "Carregar mais"
├── On Scroll: activar para infinity
└── Starting From: 1
```
### Ordenação Dinâmica (Frontend)
```
Adicionar widget: JetSmartFilters > Sorting
├── Ligar ao mesmo Listing Grid
└── Opções: Date ASC/DESC, Title, Price, Custom Field
```
---
## Listing Map
### Configuração
```
1. Activar Google Maps API Key:
JetEngine > Settings > Google Maps API Key
2. Criar Meta Box com campo tipo Map no CPT
(O utilizador define latitude/longitude via picker no editor)
3. Criar Listing Template tipo: Map Marker
Adicionar: Post Title, info a mostrar no popup
4. Adicionar widget Listing Map ao Elementor
├── Listing Item: [marker template]
├── Use Custom Query: YES
├── Map Field: seleccionar campo coordenadas
└── Map Options: zoom, altura, cluster
```
### Mapa + Listing Grid Sincronizados
```
Adicionar os dois widgets na mesma página
Ligar ao mesmo Query Builder
Activar "Synchronize with Listing Grid": YES no Map
→ Ao fazer hover no card, destaca pin no mapa
→ Ao fazer hover no pin, destaca card na lista
```
---
## Archive e Single Templates
### Single CPT com Elementor (JetThemeCore ou Elementor Pro)
```
JetThemeCore > Templates > Add New > Single
Conditions: Post Type = [meu_cpt]
Editar com Elementor usando Dynamic Tags
OU
Elementor > Templates > Theme Builder > Single
Conditions: igual
```
### Archive Page
```
JetThemeCore > Templates > Add New > Archive
Conditions: Post Type Archive = [meu_cpt]
Adicionar:
├── Archive Title Widget
├── Listing Grid Widget (sem Custom Query — usa WP_Query do arquivo)
└── JetSmartFilters (ligado ao mesmo archive)
```
---
## JetSmartFilters — Integração Avançada
### AJAX Reload vs Full Page
```
Provider: Listing Grid → AJAX (sem reload de página)
Provider: Archive → Pode ser AJAX ou URL based
Provider: WooCommerce → AJAX
```
### URL Based Filters (SEO Friendly)
```
JetSmartFilters > Filters > Edit
├── Query Variable: activar
├── URL Based: activar
└── Clean URL: activar
Resultado: site.pt/imoveis/?cidade=Lisboa&preco=200000-400000
(Em vez de AJAX puro — melhor para SEO e partilha de URL)
```
### Hierarquical Taxonomy Filter
```
Exemplo: País → Distrito → Concelho
├── Criar taxonomia com hierarquia
├── Filtro: Checkboxes com hierarquia activada
└── "Show children of selected": activar
```
---
*JetEngine Query & Listings | Descomplicar® | 18-02-2026*