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,572 @@
---
name: branda-menu
description: >
Organiza o menu admin WordPress via Branda (branda-white-labeling) em seccoes Descomplicar standard. Computa Branda IDs a partir de slugs WP, constroi menu custom com itens nativos reordenados + section headers, e guarda programaticamente via WP-CLI. Use when "branda menu", "organizar menu admin", "menu wordpress", "admin sidebar", "reorganizar admin".
author: Descomplicar Crescimento Digital
version: 1.0.0
quality_score: 85
user_invocable: true
category: wordpress
tags: [branda, wordpress, admin-menu, white-labeling, wip]
desk_project: 69
allowed-tools: Read, Write, Edit, Bash, mcp__ssh-unified, mcp__memory-supabase
mcps: ssh-unified, memory-supabase
dependencies:
mcps: [ssh-unified]
plugins: [branda-white-labeling]
triggers:
- "User mentions 'branda menu', 'organizar menu', 'admin sidebar'"
- "User asks to reorganize WordPress admin menu"
- "Setting up new WiP site admin customization"
---
# /branda-menu - Organizar Menu Admin WordPress
Skill para organizar o menu admin sidebar de qualquer site WordPress WiP usando o Branda (branda-white-labeling).
---
## Arquitectura Branda (Referencia Interna)
### Como o Branda gere menus
O Branda usa um modulo `admin/menu.php` que intercepta o render do menu admin WordPress e substitui pela configuracao custom.
**Options `wp_options` envolvidas:**
| Option | Funcao |
|--------|--------|
| `ultimatebranding_activated_modules` | Modulos activos (precisa ter `admin/menu.php`) |
| `ub_custom_admin_menu` | Configuracao do menu custom por role |
| `ub_saved_admin_menus` | Captura do menu nativo WP (so para UI Branda, nao para render) |
### Algoritmo de ID (critico)
O Branda mapeia slugs WordPress para IDs internos usando:
```php
function branda_id($slug) {
return "menu_item_" . substr(base_convert(md5($slug), 16, 32), 0, 12);
}
```
**Exemplo:** `index.php` -> `menu_item_42ho017e7jo0`
Este algoritmo e determinista. O mesmo slug gera sempre o mesmo ID, independente do site.
### Tipos de itens no menu
| Tipo | Campos chave | Uso |
|------|-------------|-----|
| **Section header** | `link_type: "none"`, `css_classes: "menu-highlight"` | Separador visual com titulo |
| **Custom link** | `link_type: "custom"`, `custom_url: "..."` | Link manual (ex: Descomplicar) |
| **Native item** | `was_native: 1`, campos vazios | Item WP real, nome preenchido dinamicamente |
| **Hidden item** | `is_hidden: "1"`, `was_native: 1` | Item WP escondido do menu |
### Mecanismo de merge (parse_args_deep)
Na renderizacao, o Branda faz merge do menu configurado com o menu WP real:
- Itens nativos no config: aparecem na posicao configurada
- Itens nativos **nao** no config: inseridos na posicao default (aparecem misturados!)
- Itens custom: aparecem na posicao configurada
- Submenus vazios `[]`: auto-preenchidos com submenus reais do WP
**Regra critica:** Incluir **todos** os itens nativos no config (visiveis na posicao desejada OU hidden). Se faltar algum, aparece na posicao default e quebra a organizacao.
---
## Seccoes Standard Descomplicar (9)
Todo site WiP deve ter o menu organizado nestas 9 seccoes, nesta ordem:
| # | Seccao | Dashicon | Conteudo tipico |
|---|--------|----------|----------------|
| 1 | **Suporte** | dashicons-admin-tools | Link Descomplicar (desk.descomplicar.pt) |
| 2 | **Admin** | dashicons-admin-generic | Painel, Utilizadores, Opcoes, Plugins, Ferramentas |
| 3 | **Conteudo** | dashicons-admin-page | Paginas, CPTs do projecto, Artigos, Multimedia, ACF |
| 4 | **Design** | dashicons-admin-appearance | Elementor, Modelos, Jeg Kit, Apresentacao |
| 5 | **Marketing** | dashicons-megaphone | FluentCRM, Fluent Forms, Rank Math, WhatsApp, etc. |
| 6 | **Idiomas** | dashicons-translation | Polylang/WPML, Loco Translate (se multilingue) |
| 7 | **Performance** | dashicons-performance | WP Fastest Cache, WebP Express |
| 8 | **Seguranca** | dashicons-shield | Wordfence, Complianz |
| 9 | **WebMaster** | dashicons-admin-settings | Branda, Code Snippets, Activity Log |
**Notas:**
- Seccao Idiomas so aparece se site for multilingue
- CPTs especificos do projecto (Tours, Servicos, etc.) vao em Conteudo
- Plugins sectoriais (KiviCare, WooCommerce, etc.) podem ter seccao propria entre Marketing e Idiomas
---
## Itens tipicamente hidden
Estes itens nativos devem ser incluidos no config com `is_hidden: "1"`:
| Slug | Motivo |
|------|--------|
| `separator1`, `separator2`, `separator-last` | Separadores nativos substituidos por section headers |
| `edit-comments.php` | Comentarios raramente usados |
| `hello-elementor` | Pagina tema redundante com themes.php |
| `elementor` | Menu Elementor duplicado (ja existe elementor-home) |
---
## Procedimento
### Passo 1: Verificar pre-requisitos
```bash
# Via MCP SSH - verificar Branda activo e modulo menu activo
mcp__ssh-unified__ssh_execute server:"server" command:"wp plugin is-active branda-white-labeling --allow-root --path=/home/USER/SITE && echo ACTIVE || echo INACTIVE"
# Verificar modulo menu activo
mcp__ssh-unified__ssh_execute server:"server" command:"wp eval '
\$m = get_option(\"ultimatebranding_activated_modules\", array());
echo isset(\$m[\"admin/menu.php\"]) ? \"MENU MODULE ACTIVE\" : \"MENU MODULE INACTIVE\";
' --allow-root --path=/home/USER/SITE"
```
Se modulo inactivo, activar:
```bash
mcp__ssh-unified__ssh_execute server:"server" command:"wp eval '
\$m = get_option(\"ultimatebranding_activated_modules\", array());
\$m[\"admin/menu.php\"] = \"yes\";
update_option(\"ultimatebranding_activated_modules\", \$m);
echo \"Activado\";
' --allow-root --path=/home/USER/SITE"
```
### Passo 2: Extrair menu nativo
Obter lista de plugins activos e menu WP via WP-CLI. Como WP-CLI nao carrega todos os menus admin, usar este metodo combinado:
**Opcao A (preferida): mu-plugin temporario**
```bash
# Criar mu-plugin que captura menu no proximo page load
mcp__ssh-unified__ssh_execute server:"server" command:"cat > /home/USER/SITE/wp-content/mu-plugins/capture-menu.php << 'PHPEOF'
<?php
/**
* Temporary mu-plugin to capture native admin menu for Branda
* Auto-removes after capture
*/
add_action('admin_menu', function() {
if (!current_user_can('manage_options')) return;
if (get_option('_branda_menu_captured')) return;
global \$menu, \$submenu;
// Save raw menu data
update_option('_branda_menu_capture', array(
'menu' => \$menu,
'submenu' => \$submenu,
'captured_at' => time()
));
update_option('_branda_menu_captured', true);
// Self-destruct
@unlink(__FILE__);
}, 999999);
PHPEOF
echo 'mu-plugin criado'
"
```
Depois, gerar cookie de auth e disparar um page load admin:
```bash
# Gerar cookie auth via WP-CLI
mcp__ssh-unified__ssh_execute server:"server" command:"wp eval '
\$expiration = time() + 300;
\$cookie = wp_generate_auth_cookie(1, \$expiration, \"logged_in\");
echo \$cookie;
' --allow-root --path=/home/USER/SITE"
# Disparar page load admin com curl (substituir COOKIE e HASH)
mcp__ssh-unified__ssh_execute server:"server" command:"curl -s -o /dev/null -w '%{http_code}' -b 'wordpress_logged_in_HASH=COOKIE' https://SITE/wp-admin/"
```
Depois ler a captura:
```bash
mcp__ssh-unified__ssh_execute server:"server" command:"wp eval '
\$cap = get_option(\"_branda_menu_capture\");
if (\$cap) {
foreach (\$cap[\"menu\"] as \$pos => \$item) {
if (count(\$item) >= 3) {
\$slug = \$item[2];
\$title = strip_tags(\$item[0]);
echo \"\$pos. \$title => \$slug\n\";
}
}
}
' --allow-root --path=/home/USER/SITE"
```
**Opcao B (alternativa): Chrome DevTools**
Se tiver acesso ao browser, navegar ao wp-admin e executar JS:
```javascript
Array.from(document.querySelectorAll('#adminmenu > li')).forEach((li, i) => {
const a = li.querySelector('a');
if (a) {
const name = a.querySelector('.wp-menu-name')?.textContent?.trim() || a.textContent?.trim();
const href = a.getAttribute('href') || '';
console.log(i + '. ' + name + ' => ' + href);
}
});
```
### Passo 3: Mapear itens para seccoes
Com a lista de itens nativos, mapear cada um para a seccao correcta.
**Mapeamento standard por slug:**
```
# Admin
index.php -> Admin
users.php -> Admin
options-general.php -> Admin
plugins.php -> Admin
tools.php -> Admin
# Conteudo
edit.php -> Conteudo (Artigos)
edit.php?post_type=page -> Conteudo (Paginas)
upload.php -> Conteudo (Multimedia)
edit.php?post_type=acf-field-group -> Conteudo (ACF)
edit.php?post_type=* -> Conteudo (CPTs)
# Design
elementor-home -> Design (Elementor)
edit.php?post_type=elementor_library -> Design (Modelos)
jkit -> Design (Jeg Kit)
themes.php -> Design (Apresentacao)
# Marketing
fluentcrm-admin -> Marketing
fluent_forms -> Marketing
rank-math -> Marketing
click-to-chat -> Marketing
# Idiomas (se multilingue)
mlang -> Idiomas (Polylang)
loco -> Idiomas (Loco Translate)
# Performance
wpfastestcacheoptions -> Performance
# Seguranca
Wordfence -> Seguranca
complianz -> Seguranca
# WebMaster
branding -> WebMaster (Branda)
wpcode -> WebMaster (Code Snippets)
# Hidden
separator1, separator2, separator-last -> Hidden
edit-comments.php -> Hidden
hello-elementor -> Hidden
elementor -> Hidden (duplicado de elementor-home)
```
**Itens que nao encaixam no standard:** Perguntar ao utilizador em que seccao colocar. Exemplos: WooCommerce, KiviCare, FareHarbor.
### Passo 4: Construir e guardar o menu
Criar um PHP script no servidor e executar com `wp eval-file`:
```bash
mcp__ssh-unified__ssh_execute server:"server" command:"cat > /tmp/branda-menu-SITE.php << 'PHPEOF'
<?php
/**
* Branda menu builder for SITENAME
* Generated by /branda-menu skill
*/
function branda_id(\$slug) {
return 'menu_item_' . substr(base_convert(md5(\$slug), 16, 32), 0, 12);
}
function native_item(\$subs = array()) {
return array(
'title' => '', 'id_attribute' => '', 'css_classes' => '',
'icon_svg' => '', 'icon_url' => '', 'icon_image_id' => '',
'dashicon' => '', 'icon_type' => '', 'custom_url' => '',
'link_type' => '', 'link_target' => '', 'is_invisible' => '',
'is_hidden' => '', 'was_native' => 1, 'submenu' => \$subs
);
}
function hidden_item() {
\$item = native_item();
\$item['is_hidden'] = '1';
return \$item;
}
function section_header(\$title, \$dashicon) {
return array(
'icon_type' => 'dashicon', 'link_type' => 'none',
'submenu' => array(), 'title' => \$title,
'css_classes' => 'menu-highlight', 'dashicon' => \$dashicon
);
}
function custom_link(\$title, \$url, \$dashicon, \$target = '') {
return array(
'icon_type' => 'dashicon', 'link_type' => 'custom',
'submenu' => array(), 'title' => \$title,
'dashicon' => \$dashicon, 'link_target' => \$target,
'custom_url' => \$url
);
}
// === BUILD MENU ===
\$menu = array();
// 1. SUPORTE
\$menu['menu_item_sec_suporte'] = section_header('Suporte', 'dashicons-admin-tools');
\$menu['menu_item_desk_link'] = custom_link('Descomplicar', 'https://desk.descomplicar.pt/', 'dashicons-admin-comments', '_blank');
// 2. ADMIN
\$menu['menu_item_sec_admin'] = section_header('Admin', 'dashicons-admin-generic');
\$menu[branda_id('index.php')] = native_item();
\$menu[branda_id('users.php')] = native_item();
\$menu[branda_id('options-general.php')] = native_item();
\$menu[branda_id('plugins.php')] = native_item();
\$menu[branda_id('tools.php')] = native_item();
// 3. CONTEUDO
\$menu['menu_item_sec_conteudo'] = section_header('Conteudo', 'dashicons-admin-page');
\$menu[branda_id('edit.php?post_type=page')] = native_item();
// [INSERIR CPTs DO PROJECTO AQUI]
\$menu[branda_id('edit.php')] = native_item();
\$menu[branda_id('upload.php')] = native_item();
\$menu[branda_id('edit.php?post_type=acf-field-group')] = native_item();
// 4. DESIGN
\$menu['menu_item_sec_design'] = section_header('Design', 'dashicons-admin-appearance');
\$menu[branda_id('elementor-home')] = native_item();
\$menu[branda_id('edit.php?post_type=elementor_library')] = native_item();
// \$menu[branda_id('jkit')] = native_item(); // Se Jeg Kit activo
\$menu[branda_id('themes.php')] = native_item();
// 5. MARKETING
\$menu['menu_item_sec_marketing'] = section_header('Marketing', 'dashicons-megaphone');
\$menu[branda_id('fluentcrm-admin')] = native_item();
\$menu[branda_id('fluent_forms')] = native_item();
\$menu[branda_id('rank-math')] = native_item();
\$menu[branda_id('click-to-chat')] = native_item();
// 6. IDIOMAS (se multilingue)
// \$menu['menu_item_sec_idiomas'] = section_header('Idiomas', 'dashicons-translation');
// \$menu[branda_id('mlang')] = native_item();
// \$menu[branda_id('loco')] = native_item();
// 7. PERFORMANCE
\$menu['menu_item_sec_performance'] = section_header('Performance', 'dashicons-performance');
\$menu[branda_id('wpfastestcacheoptions')] = native_item();
// 8. SEGURANCA
\$menu['menu_item_sec_seguranca'] = section_header('Seguranca', 'dashicons-shield');
\$menu[branda_id('Wordfence')] = native_item();
\$menu[branda_id('complianz')] = native_item();
// 9. WEBMASTER
\$menu['menu_item_sec_webmaster'] = section_header('WebMaster', 'dashicons-admin-settings');
\$menu[branda_id('branding')] = native_item();
\$menu[branda_id('wpcode')] = native_item();
// HIDDEN
\$menu[branda_id('separator1')] = hidden_item();
\$menu[branda_id('separator2')] = hidden_item();
\$menu[branda_id('separator-last')] = hidden_item();
\$menu[branda_id('edit-comments.php')] = hidden_item();
\$menu[branda_id('hello-elementor')] = hidden_item();
\$menu[branda_id('elementor')] = hidden_item();
// SAVE
update_option('ub_custom_admin_menu', array('administrator' => \$menu));
wp_cache_flush();
\$visible = count(array_filter(\$menu, function(\$i) { return empty(\$i['is_hidden']); }));
\$hidden = count(\$menu) - \$visible;
echo \"Menu guardado: \$visible visiveis + \$hidden hidden = \" . count(\$menu) . \" total\n\";
PHPEOF
echo 'Script criado'
"
# Executar
mcp__ssh-unified__ssh_execute server:"server" command:"wp eval-file /tmp/branda-menu-SITE.php --allow-root --path=/home/USER/SITE"
```
### Passo 5: Verificar
```bash
# Contar itens no config
mcp__ssh-unified__ssh_execute server:"server" command:"wp eval '
\$m = get_option(\"ub_custom_admin_menu\");
\$items = \$m[\"administrator\"];
echo \"Total: \" . count(\$items) . \"\n\";
foreach (\$items as \$key => \$item) {
\$hidden = !empty(\$item[\"is_hidden\"]) ? \" [HIDDEN]\" : \"\";
\$type = !empty(\$item[\"was_native\"]) ? \"native\" : (!empty(\$item[\"link_type\"]) && \$item[\"link_type\"] == \"none\" ? \"header\" : \"custom\");
\$title = !empty(\$item[\"title\"]) ? \$item[\"title\"] : \"(native)\";
echo \"\$key => \$type: \$title\$hidden\n\";
}
' --allow-root --path=/home/USER/SITE"
```
Verificar visualmente no browser: navegar ao wp-admin e confirmar que o menu esta organizado nas 9 seccoes.
### Passo 6: Limpar
```bash
# Remover script temporario
mcp__ssh-unified__ssh_execute server:"server" command:"rm -f /tmp/branda-menu-SITE.php"
# Limpar captura temporaria (se usou mu-plugin)
mcp__ssh-unified__ssh_execute server:"server" command:"wp eval '
delete_option(\"_branda_menu_capture\");
delete_option(\"_branda_menu_captured\");
echo \"Limpo\";
' --allow-root --path=/home/USER/SITE"
# Garantir mu-plugin removido
mcp__ssh-unified__ssh_execute server:"server" command:"rm -f /home/USER/SITE/wp-content/mu-plugins/capture-menu.php 2>/dev/null; echo ok"
```
---
## Adaptar para sites especificos
### Sites multilingue (Polylang/WPML)
Descomentar seccao Idiomas no script. Adicionar:
```php
$menu['menu_item_sec_idiomas'] = section_header('Idiomas', 'dashicons-translation');
$menu[branda_id('mlang')] = native_item(); // Polylang
$menu[branda_id('loco')] = native_item(); // Loco Translate
```
### Sites WooCommerce
Adicionar seccao e-Commerce entre Marketing e Idiomas:
```php
$menu['menu_item_sec_ecommerce'] = section_header('e-Commerce', 'dashicons-cart');
$menu[branda_id('woocommerce')] = native_item();
// $menu[branda_id('wc-admin&path=/analytics/overview')] = native_item(); // Analytics WC
```
### Sites Care (KiviCare)
Adicionar seccao Clinica entre Marketing e Idiomas:
```php
$menu['menu_item_sec_clinica'] = section_header('Clinica', 'dashicons-heart');
$menu[branda_id('kivicare-...')] = native_item(); // Verificar slug exacto
```
### CPTs customizados
Adicionar na seccao Conteudo, antes de Artigos:
```php
$menu[branda_id('edit.php?post_type=SLUG_CPT')] = native_item();
```
### Itens desconhecidos
Se o menu nativo tiver itens que nao encaixam no mapeamento standard:
1. Perguntar ao utilizador onde colocar
2. Se plugin temporario/teste: adicionar ao Hidden
3. Se plugin permanente: criar seccao propria ou adicionar a seccao existente
---
## Troubleshooting
### Menu nao muda apos guardar
1. Limpar cache: `wp cache flush --allow-root`
2. Verificar modulo activo: `admin/menu.php` em `ultimatebranding_activated_modules`
3. Verificar role: config usa key `administrator` (nao `admin`)
### Itens nativos aparecem fora das seccoes
Causa: item nativo nao incluido no config (inserido na posicao default pelo parse_args_deep).
Fix: Identificar o slug do item, computar o Branda ID, adicionar ao config (visivel ou hidden).
### Separadores nativos aparecem
Causa: separadores com slugs diferentes dos standard (`separator1`, `separator2`, `separator-last`).
Fix: Verificar slugs via captura do menu nativo, adicionar como hidden.
### Menu items custom aparecem em duplicado
Causa: o mesmo item existe como custom link E como native item.
Fix: Remover o custom link, manter apenas o native item na posicao correcta.
### CSS para esconder separadores em falta
Adicionar ao CSS custom do Branda (ub_admin_css) como fallback:
```css
#adminmenu li.wp-menu-separator { display: none !important; }
```
---
## IDs de slugs comuns (referencia rapida)
| Slug | Branda ID |
|------|-----------|
| `index.php` | `menu_item_42ho017e7jo0` |
| `edit.php` | `menu_item_6lk5pbiakha0` |
| `upload.php` | `menu_item_1jkdde99dfd0` |
| `edit.php?post_type=page` | `menu_item_774p5endtlu0` |
| `themes.php` | `menu_item_7jgmlsspgv60` |
| `plugins.php` | `menu_item_5g2kqk93qi30` |
| `users.php` | `menu_item_gajld83c8es0` |
| `tools.php` | `menu_item_3t0no8pv5bfg` |
| `options-general.php` | `menu_item_d1a8rsor9700` |
| `edit-comments.php` | `menu_item_252pn6seih20` |
| `separator1` | `menu_item_3u7nva84d1i0` |
| `separator2` | `menu_item_6lm7mo14a4r0` |
| `separator-last` | `menu_item_74g99t5jejn0` |
| `elementor-home` | `menu_item_2kehh8g6nop0` |
| `elementor` | `menu_item_27qkhd7iqao0` |
| `edit.php?post_type=elementor_library` | `menu_item_3rubghs8krfg` |
| `hello-elementor` | `menu_item_3q7v6ask7gpg` |
| `fluentcrm-admin` | `menu_item_4a7t8bi9mt30` |
| `fluent_forms` | `menu_item_ebai6etubd00` |
| `rank-math` | `menu_item_148bl1t91os0` |
| `click-to-chat` | `menu_item_1b65lubbpnd8` |
| `Wordfence` | `menu_item_1077vi8mf9b0` |
| `complianz` | `menu_item_5etmjgu9lnk0` |
| `wpfastestcacheoptions` | `menu_item_4hqsn1kbum10` |
| `branding` | `menu_item_7qde2b2f7670` |
| `wpcode` | `menu_item_69igp9fj4tl0` |
| `loco` | `menu_item_2c34vb7r1csg` |
| `mlang` | `menu_item_24um676vv08g` |
| `woocommerce` | Computar: `branda_id('woocommerce')` |
| `edit.php?post_type=acf-field-group` | `menu_item_70ga2p32mc70` |
Para slugs nao listados, computar com:
```bash
wp eval 'echo "menu_item_" . substr(base_convert(md5("SLUG"), 16, 32), 0, 12);' --allow-root --path=/home/USER/SITE
```
---
## Checklist
- [ ] Branda activo e modulo `admin/menu.php` activo
- [ ] Menu nativo extraido (todos os itens identificados)
- [ ] Todos os itens nativos incluidos no config (visiveis ou hidden)
- [ ] 9 seccoes criadas na ordem standard
- [ ] CPTs do projecto na seccao Conteudo
- [ ] Verificado visualmente no browser
- [ ] Script temporario e capturas removidos
- [ ] Permissoes corrigidas: `chown -R USER:USER /home/USER/SITE/`
---
*Skill v1.0.0 | 26-02-2026 | Descomplicar*

View File

@@ -0,0 +1,241 @@
---
name: crocoblock
description: >
Crocoblock ecosystem development with all JetPlugins for Elementor. Covers plugin selection, licensing, installation,
configuration and integration between JetEngine, JetSmartFilters, JetWooBuilder, JetElements, JetPopup, JetFormBuilder,
JetTabs, JetBlocks, JetSearch, JetBooking, JetThemeCore, JetReviews, JetTricks, JetCompareWishlist.
This skill should be used when the user asks about "crocoblock", "jet plugins", "jetelements", "jetwoobuilder",
"jetpopup", "jetsmartfilters", "jettabs", "jetformbuilder", "jetblocks", "jetbooking", "jetreviews",
"jetthemecore", "crocoblock license", "instalar crocoblock", "activar jet plugins", "ecosystem crocoblock",
"crocoblock subscription", "jet plugin escolher", "qual jet plugin usar", "crocoblock vs", "filtros ajax elementor",
"listing woocommerce elementor", "popup elementor crocoblock", "booking elementor", "reviews elementor".
author: Descomplicar® Crescimento Digital
version: 1.0.0
user_invocable: true
tags: [wordpress, elementor, crocoblock, jetengine, jetelements, jetsmartfilters, jetformbuilder]
allowed-tools: Read, Write, Edit, Bash, mcp__ssh-unified__ssh_execute, mcp__notebooklm__notebook_query, mcp__dify-kb__dify_kb_retrieve_segments
category: dev
quality_score: 80
updated: "2026-02-18T00:00:00Z"
---
# /crocoblock — Ecossistema Crocoblock + JetPlugins
Desenvolvimento com o ecossistema Crocoblock: suite de plugins para Elementor cobrindo CPT, filtros AJAX, WooCommerce, forms, popups, booking e muito mais.
## Matriz de Plugins
| Plugin | Função Principal | Quando Usar |
|--------|-----------------|-------------|
| **JetEngine** | CPT, Meta Boxes, Relations, Query Builder, Listings, Dynamic Content | Qualquer site com conteúdo dinâmico/estruturado |
| **JetSmartFilters** | Filtros AJAX para listings e arquivos | Catálogos, portfolios, diretórios filtráveis |
| **JetWooBuilder** | Templates WooCommerce com Elementor | Lojas com design personalizado de produto/categoria/checkout |
| **JetElements** | Widgets adicionais (Timeline, Progress Bar, Circle Progress, Maps) | Adicionar widgets não disponíveis no Elementor base |
| **JetPopup** | Popups com triggers avançados | Lead capture, notificações, banners condicionais |
| **JetFormBuilder** | Forms com lógica condicional + notificações | Formulários complexos, integração CRM, booking light |
| **JetTabs** | Tabs, Accordions, Toggle | Conteúdo tabulado interactivo |
| **JetBlocks** | Widgets para header/footer (nav, cart, search) | Cabeçalhos e rodapés custom no Theme Builder |
| **JetSearch** | Pesquisa AJAX avançada | Sites com necessidade de search instantâneo |
| **JetBooking** | Sistema de booking/reservas | Alojamento, serviços com calendário de disponibilidade |
| **JetThemeCore** | Theme Builder alternativo ao Elementor Pro | Theme Builder sem licença Elementor Pro |
| **JetReviews** | Reviews/ratings para qualquer CPT | Reviews além de WooCommerce |
| **JetTricks** | Efeitos visuais (parallax, hotspot, unfold) | Elementos visuais avançados |
| **JetCompareWishlist** | Comparar e wishlist para produtos/CPT | E-commerce ou catálogos com comparação |
## Licença e Instalação
### Subscrição Crocoblock
```
Planos: Single Site / 3 Sites / Unlimited Sites (anual ou lifetime)
Todos os planos incluem todos os plugins + atualizações
Dashboard: account.crocoblock.com > Downloads
```
### Instalação (CWP)
```bash
PHP="/opt/alt/php-fpm83/usr/bin/php"
WP="$PHP /usr/local/bin/wp --allow-root --path=/home/USER/public_html"
# 1. Upload do ZIP via WP Admin ou WP-CLI
$WP plugin install /tmp/jet-engine.zip --activate
# 2. Activar licença (via WP Admin > JetPlugins Licences)
# Cada plugin tem campo de licença separado
# Ou activar globalmente em Crocoblock > License
# 3. Verificar versões
$WP plugin list --search="jet*" --fields=name,version,status --format=table
```
### Ordem de Instalação Recomendada
```
1. JetEngine (base do ecossistema — criar antes de outros)
2. JetSmartFilters (depende do JetEngine para filtrar listings)
3. JetElements (widgets independentes)
4. JetWooBuilder (se WooCommerce presente)
5. JetFormBuilder (forms independentes)
6. JetPopup (depende de Elementor Pro ou JetThemeCore)
7. JetBlocks (apenas se Theme Builder activo)
8. Restantes (conforme necessidade)
```
## Integrações Principais
### JetEngine + JetSmartFilters (padrão mais comum)
```
JetEngine Query Builder → Listing Grid → JetSmartFilters
Filtros ligados ao mesmo Query Builder ID
```
**Passos:**
1. Criar CPT no JetEngine
2. Criar Query Builder (tipo: Posts)
3. Criar Listing Template com Elementor
4. Adicionar Listing Grid ao Elementor (apontar para o Query)
5. Adicionar Filter Widget e ligar ao mesmo Listing Grid
### JetEngine + JetWooBuilder
```
CPT Produto (WooCommerce) → JetEngine Meta Boxes → JetWooBuilder Template
Campos custom visíveis no template
```
### JetEngine + JetFormBuilder
```
JetFormBuilder → Submit → Create/Update JetEngine Post
→ Notificação email
→ Redirect
```
### JetPopup Triggers Disponíveis
| Trigger | Uso |
|---------|-----|
| On page load | Newsletter, anúncio |
| Exit intent | Retenção de visitante |
| Scroll (%) | Conteúdo contextual |
| Click on element | Mais informação inline |
| After inactivity | Re-engagement |
| User logged in/out | Conteúdo condicional |
## Dify KB — Consultar Antes de Implementar
```javascript
// Dataset Crocoblock (primário)
mcp__dify-kb__dify_kb_retrieve_segments({
dataset_id: "bdf85c26-1824-4021-92d1-be20501b35ac",
query: "[componente ou funcionalidade]"
})
// Dataset Crocoblock alternativo
mcp__dify-kb__dify_kb_retrieve_segments({
dataset_id: "139cdf67-afce-46ec-9ccd-2a06040e5b9d",
query: "[componente ou funcionalidade]"
})
```
## Troubleshooting Comum
### Plugin não activa após upload
```bash
# Verificar se Elementor está activo (dependência)
$WP plugin list --search=elementor --fields=name,status
# Verificar erros PHP
$WP eval 'error_reporting(E_ALL); ini_set("display_errors", 1);' --allow-root --path=$PATH
# Limpar transients após activar
$WP transient delete --all --allow-root --path=$PATH
```
### Conflito entre versões
```
Regra: Manter todos os JetPlugins na mesma major version
Verificar: account.crocoblock.com > Changelogs para breaking changes
```
### Listings não actualizam após filtrar
```bash
# Regenerar CSS Elementor
$WP elementor flush-css --regenerate --allow-root --path=$PATH
# Limpar cache de objecto
$WP cache flush --allow-root --path=$PATH
```
## Padrões de Uso por Tipo de Site
### Site Corporativo / Portfolio
```
JetEngine (CPT: Projecto, Serviço, Testemunho)
+ JetSmartFilters (filtrar por categoria/tag)
+ JetElements (timeline, animações)
```
### E-commerce Avançado
```
WooCommerce + JetEngine (meta boxes extra)
+ JetWooBuilder (templates produto, categoria, cart)
+ JetCompareWishlist
+ JetSmartFilters (filtros loja)
```
### Directório / Listagem de Negócios
```
JetEngine (CPT: Negócio, com Relations para Categoria/Localização)
+ JetSmartFilters (filtros AJAX por localização, categoria, preço)
+ JetSearch (pesquisa instantânea)
+ JetEngine Maps (mapa de listagens)
```
### Portal com Accounts de Utilizador
```
JetEngine (CPT, Profile Builder)
+ JetFormBuilder (submissão de conteúdo por utilizador)
+ JetEngine Relations (conteúdo associado ao utilizador)
```
### Site com Reservas
```
JetBooking (calendário + disponibilidade)
+ JetFormBuilder (form de reserva)
+ JetEngine (meta boxes de propriedade/serviço)
```
## JetEngine MCP Server (v3.8.0+)
A partir do JetEngine 3.8.0, existe integração nativa com agentes IA via **MCP Server**:
```
WP Admin > JetEngine > AI Command Center > Enable MCP Server
→ Claude pode inspecionar schema completo (CPTs, CCTs, Relações, Queries)
→ e criar/modificar estruturas via linguagem natural
```
Usar `references/automation.md` para detalhes sobre Formless Actions, JetSmartFilters Indexer e pipelines de automação.
## Referências Adicionais
- **`references/plugins.md`** — Referência detalhada de cada plugin (widgets, configurações, casos de uso avançados)
- **`references/patterns.md`** — Padrões de integração avançados (multi-relation, filtros aninhados, profile builder completo)
- **`references/automation.md`** — MCP Server nativo, JetFormBuilder Formless Actions, JetSmartFilters Indexer, Kits export/import, cache e riscos críticos
---
**Versão**: 1.1.0 | **Autor**: Descomplicar® | **Data**: 18-02-2026

View File

@@ -0,0 +1,294 @@
# Crocoblock — Automação Headless e Integrações Avançadas
## 1. JetEngine 3.8.0 — MCP Server Nativo
A partir da versão 3.8.0, o JetEngine inclui um **MCP Server nativo** que expõe a topologia completa do site a agentes IA (Claude, Copilot, Cursor).
### Activar o MCP Server
```
WP Admin > JetEngine > AI Command Center > Enable MCP Server
→ URL do servidor: https://site.pt/wp-json/jet-engine/v1/mcp
→ Autenticação: Application Password
```
### Capacidades do MCP
| Capacidade | Descrição |
|-----------|-----------|
| Inspecção de schema | Lista CPTs, CCTs, Meta Boxes, Taxonomias, Relações |
| Criação de CPT | Instanciar novo Custom Post Type via prompt |
| Criação de CCT | Definir Custom Content Type com campos tipados |
| Definir relações | Criar estruturas relacionais entre entidades |
| Query Builder | Criar queries estruturadas |
**Caso de uso:** Cliente especifica necessidades → Claude interroga MCP → constrói arquitectura completa (CPT + CCT + Relations + Queries) sem GUI.
---
## 2. JetFormBuilder — Formless Actions Endpoints
O addon **Formless Actions Endpoints** permite usar as acções de um formulário JetFormBuilder como endpoint REST puro, sem renderizar formulário no frontend.
### Como Activar
```
JetFormBuilder > Settings > Formless Actions Endpoints: Enable
Cada formulário recebe rota: POST /wp-json/jet-fb/v1/form/{form_id}
```
### Payload de Exemplo
```bash
# Submeter dados para formulário sem interface visual
# Acciona todas as Post Submit Actions configuradas (Create Post, Email, Webhook, etc.)
curl -X POST https://site.pt/wp-json/jet-fb/v1/form/1234 \
-H "Authorization: Basic dXNlcjpwYXNz..." \
-H "Content-Type: application/json" \
-d '{
"nome_cliente": "Empresa ABC",
"email": "contacto@empresaabc.pt",
"valor_contrato": 5000,
"tipo_servico": "website"
}'
```
### Encadear Acções Complexas
```
Um único request pode:
1. Criar Post no CPT "Projecto" com status pending
2. Enviar email ao admin
3. Actualizar relação Cliente↔Projecto
4. Enviar Webhook para n8n
5. Criar registo em CCT "Notificacoes"
```
### Interceptar com Hook Custom
```php
<?php
// Interceptar antes de gravar para validação externa
add_action('jet-form-builder/custom-action/analise_risco', function($request, $action_handler) {
$dados = $request->get_all();
$texto = sanitize_textarea_field($dados['descricao'] ?? '');
$post_id = absint($dados['post_id'] ?? 0);
if (empty($texto) || !$post_id) {
// Cancelar o processamento com erro
throw new \Jet_Form_Builder\Exceptions\Action_Exception('campos_obrigatorios_vazios');
}
// Validação via API externa (exemplo: análise de risco)
$score = minha_api_de_risco($texto);
if ($score > 90) {
throw new \Jet_Form_Builder\Exceptions\Action_Exception('risco_inaceitavel');
}
// Actualizar meta field com o score calculado
update_post_meta($post_id, '_score_risco', $score);
$action_handler->add_response([
'status' => 'success',
'message' => 'Processado com sucesso.',
'score' => $score,
]);
}, 10, 2);
```
### Tabela de Registos
```sql
-- JetFormBuilder guarda registo de cada submissão
SELECT *
FROM wp_jet_fb_records
WHERE form_id = 1234
ORDER BY id DESC
LIMIT 10;
```
---
## 3. JetSmartFilters — Indexer e Automação
### O Indexer
O JetSmartFilters tem um subsistema de contagem chamado **Indexer** que mapeia contadores em `jet_smart_filters_indexer`.
```sql
-- Ver contagens indexadas
SELECT * FROM jet_smart_filters_indexer
WHERE filter_id = 550
LIMIT 20;
```
**Problema:** Após actualizações SQL directas nas tabelas CCT ou wp_jet_rel_default, os contadores dos filtros ficam desactualizados.
**Solução:**
```php
<?php
// Recalcular Indexer após bulk update
add_action('meu_evento_pos_atualizacao', function() {
if (function_exists('jet_smart_filters')) {
jet_smart_filters()->indexer->index_all();
}
});
```
```bash
# Via WP-CLI
PHP="/opt/alt/php-fpm83/usr/bin/php"
WP="$PHP /usr/local/bin/wp --allow-root --path=/home/USER/public_html"
$WP eval "jet_smart_filters()->indexer->index_all();"
```
### Manipular Filtros Programaticamente
```php
<?php
// Injectar limites automáticos num filtro (ex: baseado no role do utilizador)
add_filter('jet-smart-filters/filter-instance/args', function($args, $filter_instance) {
// Só aplicar ao filtro #2550
if ((int) $args['filter_id'] !== 2550) {
return $args;
}
// Forçar range baseado em contexto (ex: permissões do utilizador)
$args['current_value'] = [
'min' => '100',
'max' => '500',
];
// Forçar AJAX
$args['apply_type'] = 'ajax';
return $args;
}, 10, 2);
```
### Forçar Re-indexação após SQL Massivo
```php
<?php
// Pipeline completo pós-sync ERP (exemplo: n8n aciona via HTTP)
add_action('rest_api_init', function() {
register_rest_route('descomplicar/v1', '/reindex', [
'methods' => 'POST',
'callback' => function() {
wp_cache_flush();
\Jet_Engine\Query_Builder\Manager::instance()->listings_cache->clear();
jet_smart_filters()->indexer->index_all();
return rest_ensure_response(['success' => true, 'message' => 'Re-indexação concluída.']);
},
'permission_callback' => function() {
return current_user_can('edit_posts');
},
]);
});
```
---
## 4. Kits e Templates — Export/Import Programático
### Estrutura de um Kit
```
Kit JSON (manifest.json):
├── CPTs com todas as configurações
├── CCTs com schema de campos
├── Meta Boxes
├── Taxonomias
├── Relações
├── Query Builder queries
├── Listing Templates (Elementor)
└── JetFormBuilder forms
```
### Export via WP-CLI
```bash
PHP="/opt/alt/php-fpm83/usr/bin/php"
WP="$PHP /usr/local/bin/wp --allow-root --path=/home/USER/public_html"
# Exportar estrutura JetEngine para JSON
$WP eval '
$cpts = get_option("jet_engine_cpt", []);
$ccls = get_option("jet_engine_ccl", []);
$rels = get_option("jet_engine_relations", []);
$metas = get_option("jet_engine_meta_boxes", []);
$kit_data = compact("cpts", "ccls", "rels", "metas");
file_put_contents("/tmp/kit-jetengine.json", json_encode($kit_data, JSON_PRETTY_PRINT));
'
echo "Kit exportado para /tmp/kit-jetengine.json"
```
### Import em Novo Ambiente
```bash
# ATENÇÃO: os IDs das relações mudam entre ambientes
# Sempre usar slugs como referência, não IDs numéricos
$WP eval-file /tmp/import-kit.php
$WP cache flush
$WP rewrite flush
```
### Pitfall de Migração — IDs de Relações
```
Problema: CCT item #45 no staging → ID #45
Mesmo item no production após import → ID #102
→ Relações wp_jet_rel_default ficam com IDs errados
Solução: Usar slug/título como referência durante migração
Converter para ID após import via get_posts(['name' => 'slug'])
Ou usar WP All Import para recriar relações com mapeamento de IDs
```
---
## 5. Integração WooCommerce + JetEngine
### Queries em Produtos WooCommerce
```
Produtos WooCommerce são CPT 'product' → compatíveis com JetEngine
Query Builder pode filtrar produtos por meta fields JetEngine adicionados
JetWooBuilder consome estas queries para listagens customizadas
```
### Relações com Produtos
```sql
-- Produtos relacionados com CPT "Fornecedor" via JetEngine Relations
SELECT p.post_title AS produto, f.post_title AS fornecedor
FROM wp_posts p
JOIN wp_jet_rel_default r ON r.child_object_id = p.ID
JOIN wp_posts f ON f.ID = r.parent_object_id
WHERE r.rel_id = 'relacao_fornecedores_produtos'
AND p.post_status = 'publish';
```
---
## 6. Limitações e Riscos Críticos
| Risco | Impacto | Mitigação |
|-------|---------|-----------|
| SQL directo em wp_postmeta serializado | **Crítico** — corrupção de dados | NUNCA. Usar PHP `unserialize → edit → serialize` |
| store_items_type = "replace" na REST API | **Destrutivo** — apaga todas as relações | Usar "update" salvo intenção explícita |
| Alterar wp_options serializados via SQL | **Crítico** — instalação WordPress corrompida | Sempre via PHP `get_option → update_option` |
| SQL sem flush de cache | Dados obsoletos em listings | Sempre `wp_cache_flush()` + `listings_cache->clear()` + `indexer->index_all()` |
| Registar meta boxes antes de init prioridade 12 | Falha silenciosa | Usar hook `jet-engine/meta-boxes/register-instances` |
| Importar Kit entre versões JetEngine diferentes | Dependências fantasma no Query Builder | Manter versões alinhadas em staging e production |
---
*Crocoblock Automation | Descomplicar® | 18-02-2026*

View File

@@ -0,0 +1,220 @@
# Crocoblock — Padrões de Integração Avançados
## Padrão 1: Directório com Filtros + Mapa
**Plugins:** JetEngine + JetSmartFilters + JetElements (Maps)
```
Arquitectura:
CPT "Negócio" (com meta: morada, coordenadas, categoria, horário)
Query Builder (posts, post_type=negocio)
┌────────────────┬──────────────────┐
│ Listing Grid │ Listing Map │
│ (cards) │ (pins) │
└────────────────┴──────────────────┘
↑ sincronizados ↑
JetSmartFilters:
- Categoria (checkbox, taxonomy)
- Localização (select, meta)
- Pesquisa (search text)
- Raio de distância (range, meta GPS)
```
**Configuração de sincronização:**
```
Listing Map > Settings:
├── Synchronize with Listing Grid: YES
└── Listing Grid Selector: ID do widget Listing Grid
JetSmartFilters > Apply For:
└── Seleccionar ID do Listing Grid
(o mapa segue automaticamente via sincronização)
```
---
## Padrão 2: Marketplace / Multi-vendor Simples
**Plugins:** JetEngine + JetFormBuilder + JetSmartFilters + Profile Builder
```
Fluxo do vendedor:
1. Registo (JetFormBuilder → Register User)
2. Login
3. Dashboard vendedor (Profile Builder)
4. Criar produto (JetFormBuilder → Create Post no CPT)
5. Produto aparece no catálogo (Listing Grid filtrado por author)
Fluxo do comprador:
1. Catálogo com filtros (JetSmartFilters)
2. Contactar vendedor (JetFormBuilder → Email ao author)
3. Ver perfil vendedor (Profile Builder page pública)
```
**Relations necessárias:**
```
User → N Produtos (one-to-many)
User → N Favoritos (many-to-many)
Produto → N Reviews (one-to-many, via JetReviews)
```
---
## Padrão 3: Portal de Imobiliário
**Plugins:** JetEngine + JetSmartFilters + JetWooBuilder (opcional) + JetBooking
```
CPT: Imóvel
Meta Boxes:
├── Preço (number)
├── Área m² (number)
├── Tipologia (select: T0/T1/T2/T3/T4+)
├── Tipo (select: venda/arrendamento)
├── Localização (taxonomy: Distrito → Concelho → Freguesia)
├── Coordenadas (map)
├── Galeria (gallery)
├── Características (checkbox-list: piscina, garagem, jardim...)
└── Agente responsável (posts: CPT Agente)
Query Builder:
├── Ordenação: data / preço / área
└── Meta Query: tipo = "arrendamento" (para página específica)
JetSmartFilters:
├── Range: Preço min-max
├── Range: Área min-max
├── Checkboxes: Tipologia
├── Select Hierarchical: Localização
├── Checkboxes: Características
└── Sorting: Preço ASC/DESC, Mais recentes
```
---
## Padrão 4: E-learning / Cursos
**Plugins:** JetEngine + JetFormBuilder + Profile Builder
```
CPTs:
├── Curso (conteúdo, preço, dutor)
├── Lição (sub-item do curso)
└── Avaliação (questionário)
Relations:
├── Curso → N Lições (one-to-many)
├── Utilizador → N Cursos Inscritos (many-to-many)
└── Utilizador → N Avaliações (one-to-many)
Profile Builder Pages:
├── Meus Cursos (Query: cursos onde utilizador está inscrito)
├── Continuar Lição (última lição acedida)
└── Certificados (download PDF após conclusão)
Automatização:
JetFormBuilder → compra → Inscrever no curso → Criar relação User↔Curso
```
---
## Padrão 5: Multi-Relation (Relações Aninhadas)
```
Exemplo: Agência → Clientes → Projectos → Tarefas
Relations JetEngine:
├── agencia_clientes: Agência (1) → Clientes (N)
├── cliente_projectos: Cliente (1) → Projectos (N)
└── projecto_tarefas: Projecto (1) → Tarefas (N)
No Listing Template de Agência:
├── Lista de Clientes (Relation Field Widget)
│ └── Dentro de cada Cliente: Lista de Projectos (nested listing)
│ └── Dentro de cada Projecto: Contador de Tarefas
Nota: Listings aninhados afectam performance.
Limitar a 2 níveis de profundidade.
Para listas longas, usar AJAX/JetSmartFilters paginado.
```
---
## Padrão 6: User-Generated Content (UGC)
**Plugins:** JetEngine + JetFormBuilder + Profile Builder + JetSmartFilters
```
Fluxo:
1. Utilizador logado → Dashboard (Profile Builder)
2. Formulário "Submeter Conteúdo" (JetFormBuilder)
→ Action: Create Post no CPT "Aviso" com status "pending"
→ Action: Email de notificação ao admin
3. Admin aprova → post_status = publish
4. Conteúdo aparece no listing público
Queries do Dashboard:
├── "Os meus avisos" → Query: author = %current_user_id% + status = any
└── "Aprovados" → Query: author = %current_user_id% + status = publish
Moderação rápida:
├── Admin lista pendentes com Listing Grid
└── JetFormBuilder inline para aprovar/rejeitar (Update Post Status)
```
---
## Padrão 7: JetBooking + WooCommerce + JetEngine
```
Fluxo de Reserva:
1. Página de serviço (CPT Serviço com meta JetEngine)
2. Calendário JetBooking (disponibilidade em tempo real)
3. Seleccionar datas → Form de reserva (JetFormBuilder ou JetBooking nativo)
4. Checkout WooCommerce (JetBooking cria produto temporário)
5. Pagamento → Reserva confirmada
6. Email de confirmação (JetBooking + SMTP)
7. Dashboard cliente (Profile Builder → Minhas Reservas)
Automatização n8n:
JetBooking webhook → n8n → Google Calendar → SMS (via Twilio)
```
---
## Anti-Padrões (Evitar)
### Demasiados JetPlugins Activos
```
Problema: Cada JetPlugin adiciona JS/CSS extra
Solução: Desactivar plugins não usados em cada site específico
Ferramenta: JetPlugins > Settings > Disable components não usados
```
### Listings Sem Paginação em Catálogos Grandes
```
Problema: Listing Grid sem paginação carrega todos os posts → PHP timeout
Solução: Sempre activar paginação ou Load More em listas >20 items
```
### JetSmartFilters Sem AJAX
```
Problema: URL-based filters recarregam a página inteira
Uso correcto: AJAX filters para UX fluida; URL-based apenas para SEO
```
### Meta Boxes em CCT Desnecessariamente
```
CCT não suporta taxonomies nativas, comentários, revisões.
Usar CPT quando precisar dessas funcionalidades WordPress.
```
---
*Crocoblock Patterns | Descomplicar® | 18-02-2026*

View File

@@ -0,0 +1,240 @@
# Crocoblock — Referência Detalhada de Plugins
## JetElements
Widgets adicionais para Elementor. Não depende do JetEngine.
### Widgets Principais
| Widget | Uso |
|--------|-----|
| Animated Box | Caixa com flip/hover reveal |
| Circle Progress | Gauge circular animado |
| Timeline | Cronologia vertical/horizontal |
| Countdown Timer | Contador regressivo |
| Scroll Navigation | Navegação por secções |
| Maps | Google Maps avançado |
| Weather | Widget de meteorologia |
| Price List | Lista de preços |
| Progress Bar | Barra de progresso |
| Slider | Slider de imagens/conteúdo |
| Portfolio | Grid de portfolio com filtros |
| Testimonials | Slider de testemunhos |
---
## JetSmartFilters
Filtros AJAX para qualquer listing (JetEngine, WooCommerce, Archive).
### Tipos de Filtros
| Tipo | Quando Usar |
|------|-------------|
| Checkboxes | Categorias múltiplas |
| Radio | Opção única (ordenação) |
| Select | Dropdown de valores |
| Range | Preço, área (slider min-max) |
| Date Range | Filtrar por datas |
| Search | Pesquisa por texto |
| Rating | Filtrar por estrelas |
| Color Picker | Filtros visuais por cor |
### Configuração Essencial
```
1. Criar filter em JetSmartFilters > Filters
2. Seleccionar Source: JetEngine Query / WC_Query / WP_Query
3. Seleccionar Field: meta field / taxonomy / post field
4. Adicionar Filter Widget ao Elementor
5. Ligar ao Listing Grid via "Apply filters for" (seleccionar ID do listing)
```
### Filtros Aninhados (Dependent Filters)
```
Exemplo: Distrito → Concelho (filho depende do pai)
- Configurar taxonomy pai e filho no JetEngine
- Em JetSmartFilters: activar "Hierarchical taxonomy filter"
- Ou usar "Visible terms" com condição dinâmica
```
---
## JetWooBuilder
Templates custom para WooCommerce via Elementor.
### Templates Disponíveis
| Template | Substitui |
|----------|-----------|
| Shop (Archive) | Página principal da loja |
| Product | Single product page |
| Cart | Página de carrinho |
| Checkout | Página de checkout |
| My Account | Dashboard do cliente |
| Thank You | Página pós-compra |
| Category | Página de categoria |
### Widgets JetWooBuilder
```
Products Grid # Grid de produtos com query custom
Product Price # Preço com styling avançado
Product Gallery # Galeria com thumbnails
Add to Cart # Botão com variantes
Product Tabs # Descrição, Reviews, Atributos
Related Products # Produtos relacionados
Mini Cart # Cart dropdown
```
### Configurar Template de Produto
```
1. JetWooBuilder > Templates > Add New
2. Template Type: Product
3. Editar com Elementor — arrastar widgets JetWoo
4. Conditions: incluir "Product" (todos) ou específico por categoria/tag
```
---
## JetFormBuilder
Forms com lógica condicional, integrações e notificações.
### Tipos de Campos
```
Text, Email, Tel, URL, Number, Textarea
Date, Time, DateTime
Select, Radio, Checkboxes
File Upload, Image Upload
Hidden Field, Calculated Field
Repeater Field (subformulário dinâmico)
```
### Acções Pós-Submit
| Acção | Função |
|-------|--------|
| Send Email | Notificação email standard |
| Send Email (Advanced) | Template HTML customizado |
| Create Post | Criar CPT (com JetEngine) |
| Update User | Actualizar user meta |
| Webhook | Enviar para n8n/Make/Zapier |
| Redirect | Redirecionar após submit |
| Register User | Criar conta WP |
| Login User | Login automático |
| MailChimp/ActiveCampaign | Subscrição newsletter |
### Formulário Silencioso (Para n8n)
```php
// Receber POST externo que passa pelas validações do JetFormBuilder
// Criar formulário com campos que correspondem ao payload
// Submeter via HTTP POST para: /wp-json/jet-form-builder/v1/process-form
// Autenticação: Application Passwords
```
---
## JetPopup
Popups com triggers e condições de display avançadas.
### Triggers
| Trigger | Config |
|---------|--------|
| On Page Load | Delay em segundos |
| Exit Intent | Detecta movimento para fora |
| Scroll to Element | Scroll até % ou selector CSS |
| Click on Element | Selector CSS do trigger |
| After Inactivity | Segundos sem interacção |
| After N Page Views | Contador de visitas |
| Once Per Session | Não repete na sessão |
### Conditions (Display)
```
URL match / URL contains
User logged in / out
User role
Device type (mobile/desktop)
WooCommerce: cart empty/not empty, specific product in cart
JetEngine: post type, taxonomy, specific post
```
---
## JetBooking
Sistema de booking com calendário de disponibilidade.
### Fluxo Base
```
1. Configurar Unidade de Booking (ex: Quarto, Serviço)
2. Definir preços (diários, por horas, temporada)
3. Definir disponibilidade e check-in/check-out rules
4. Criar template de booking com Elementor
5. Adicionar Calendário + Booking Form ao template
6. Configurar emails de confirmação
```
### Integração WooCommerce
```
JetBooking → WooCommerce Product → Checkout WooCommerce
- Preço calculado automaticamente (n noites × preço/noite)
- Stock = disponibilidade do calendário
```
---
## JetBlocks
Widgets para construir headers e footers com Elementor.
### Widgets Disponíveis
```
Navigation Menu # Menu responsivo avançado
Site Logo # Logo com dark mode toggle
Cart Button # Contador de itens no carrinho
Search Box # Pesquisa com AJAX
Auth Links # Login/Logout dinâmico
User Name # Nome do utilizador logado
Breadcrumbs # Fil d'Ariane
Mobile Menu # Menu hamburger
```
---
## JetThemeCore
Theme Builder alternativo ao Elementor Pro (para licenças Free).
### Templates
```
Header / Footer
Single (Posts, CPT)
Archive (Category, Tag, CPT Archive)
Search Results
404
```
### Diferença vs Elementor Pro Theme Builder
```
JetThemeCore: Gratuito, condições básicas
Elementor Pro: Mais condições, melhor integração com Popup Builder
Recomendação: Usar Elementor Pro se disponível
```
---
*Crocoblock Plugins Reference | Descomplicar® | 18-02-2026*

View File

@@ -2,17 +2,21 @@
name: elementor
description: >
Advanced development with Elementor Pro and Crocoblock ecosystem. Creates custom widgets, configures Theme Builder, develops with JetEngine, JetWooBuilder, optimizes performance and troubleshoots issues.
Also covers programmatic automation: deploy pipelines, kit import, replace-urls, flush-css, MySQL manipulation of _elementor_data, REST API endpoints, Dynamic Tags, CSS cache invalidation.
Use when building Elementor sites, creating custom widgets, configuring theme templates, developing with Crocoblock, or when user mentions
"elementor", "custom widget", "theme builder", "jetengine", "crocoblock", "jetwoobuilder", "page builder", "elementor pro".
"elementor", "custom widget", "theme builder", "jetengine", "crocoblock", "jetwoobuilder", "page builder", "elementor pro",
"deploy elementor", "pipeline elementor", "elementor kit import", "elementor replace-urls", "elementor flush-css",
"automação elementor", "elementor programático", "elementor mysql", "rebranding elementor", "elementor rest api",
"elementor dynamic tag", "elementor cache", "elementor agente ia", "elementor wp-cli".
author: Descomplicar® Crescimento Digital
version: 1.1.0
version: 1.2.0
user_invocable: true
tags: [wordpress, elementor, pagebuilder, crocoblock, jetengine, widgets]
tags: [wordpress, elementor, pagebuilder, crocoblock, jetengine, widgets, automation, wpcli]
desk_task: 1478
allowed-tools: Read, Write, Edit, Bash, mcp__ssh-unified__ssh_execute, mcp__dify-kb__dify_kb_retrieve_segments
allowed-tools: Read, Write, Edit, Bash, mcp__ssh-unified__ssh_execute, mcp__notebooklm__notebook_query, mcp__dify-kb__dify_kb_retrieve_segments
category: dev
quality_score: 75
updated: "2026-02-04T18:00:00Z"
quality_score: 80
updated: "2026-02-18T00:00:00Z"
---
# /elementor - Elementor Development
@@ -237,14 +241,21 @@ Elementor > Settings > Advanced > Breakpoints
## Troubleshooting
### Widget não aparece
### Widget não aparece / CSS desactualizado
```bash
# Limpar cache Elementor
wp elementor flush-css --allow-root
# CWP — SEMPRE com prefixo PHP completo
PHP="/opt/alt/php-fpm83/usr/bin/php"
WP="$PHP /usr/local/bin/wp --allow-root --path=/home/USER/public_html"
# Regenerar ficheiros CSS
wp elementor replace_urls --old=http --new=https --allow-root
# Regenerar CSS (após qualquer alteração programática)
$WP elementor flush-css --regenerate
# Substituição segura de URLs (NUNCA usar wp search-replace directamente)
$WP elementor replace-urls https://antigo.com https://novo.com
# Sincronizar BD após update do plugin
$WP elementor update db
```
### Erro após update
@@ -264,6 +275,20 @@ Elementor > Tools > Version Control > Rollback
5. Limitar widgets por página
```
## Automação Programática (WP-CLI / MySQL / REST API)
Para deploy automatizado, pipelines IA, migração em massa e manipulação programática do Elementor sem GUI, consultar:
**`references/automation.md`** — Referência completa com:
- Pipeline de deploy CWP (kit import + replace-urls + flush-css)
- Estrutura `_elementor_data` JSON e queries MySQL com `JSON_REPLACE`
- REST API custom endpoint (com `wp_slash`, cache invalidation)
- PHP Dynamic Tags para conteúdo runtime (requer Pro)
- Geração de Kit ZIP em Python
- Rebranding global via `elementor_active_kit`
- Regras críticas (NUNCA `wp search-replace` em `_elementor_data`)
- MCP Elementor (Ultimate Elementor MCP, ~60 tools)
## Datasets Dify
| Dataset | ID | Prioridade |
@@ -275,4 +300,10 @@ Elementor > Tools > Version Control > Rollback
---
**Versão**: 1.0.0 | **Autor**: Descomplicar®
## Referências Adicionais
- **`references/automation.md`** — Automação programática completa (WP-CLI pipelines, MySQL, REST API, PHP Hooks, Kits, CSS)
---
**Versão**: 1.2.0 | **Autor**: Descomplicar® | **Actualizado**: 18-02-2026

View File

@@ -0,0 +1,417 @@
# Elementor — Automação Programática e Pipelines
Referência técnica para manipular o Elementor sem GUI: WP-CLI, MySQL, REST API, PHP Hooks, Kits e CSS.
---
## 1. WP-CLI + Elementor (CWP)
### Formato Obrigatório CWP
```bash
PHP="/opt/alt/php-fpm83/usr/bin/php"
WP="$PHP /usr/local/bin/wp --allow-root --path=/home/USER/public_html"
```
### Comandos Essenciais
```bash
# Regenerar CSS (SEMPRE após qualquer alteração programática)
$WP elementor flush-css --regenerate
# Substituição segura de URLs (evita corrupção JSON)
# NUNCA usar wp search-replace directamente em _elementor_data
$WP elementor replace-urls $URL_OLD $URL_NEW
$WP search-replace $URL_OLD $URL_NEW --skip-columns=guid
# Importar Kit (design system completo)
$WP elementor kit import /tmp/kit.zip \
--include=site-settings,templates,content \
--overrideConditions=all
# Sincronizar esquema BD após update do plugin
$WP elementor update db
# Modo manutenção
$WP maintenance-mode activate
$WP maintenance-mode deactivate
```
### Pipeline de Deploy Completo
```bash
#!/bin/bash
# Pipeline de rebranding via WP-CLI (CWP)
PHP="/opt/alt/php-fpm83/usr/bin/php"
SITE_PATH="/home/USER/public_html"
KIT_PATH="/tmp/rebranding-2026.zip"
URL_OLD="https://staging.agencia.com"
URL_NEW="https://www.producao.com"
WP="$PHP /usr/local/bin/wp --allow-root --path=$SITE_PATH"
$WP maintenance-mode activate
$WP elementor kit import $KIT_PATH --include=site-settings,templates,content --overrideConditions=all
$WP elementor replace-urls $URL_OLD $URL_NEW
$WP search-replace $URL_OLD $URL_NEW --skip-columns=guid
$WP elementor flush-css --regenerate
$WP elementor update db
$WP maintenance-mode deactivate
```
### Avaliação do Método
| Critério | Análise |
|----------|---------|
| Complexidade | Baixa — sintaxe documentada, comportamento previsível |
| Segurança | Alta — invoca internos do WordPress, risco apenas em SSH |
| Usar quando | Deploy, purga de cache, migração de URLs, sincronização BD |
---
## 2. Manipulação Directa via MySQL
### Estrutura _elementor_data
O Elementor guarda layouts como JSON em `wp_postmeta`. Três meta keys obrigatórias:
| Meta Key | Valor |
|----------|-------|
| `_elementor_edit_mode` | `builder` |
| `_elementor_template_type` | `wp-page`, `kit`, `header`, `footer` |
| `_elementor_data` | JSON array com toda a árvore de widgets |
### Estrutura JSON Interna
```json
[{
"id": "3130e2cf",
"elType": "container",
"isInner": false,
"settings": { ... },
"elements": [{
"id": "a1b2c3d4",
"elType": "widget",
"widgetType": "button",
"settings": {
"text": "Clique aqui",
"link": { "url": "https://exemplo.com" }
}
}]
}]
```
### Query MySQL com JSON_REPLACE
```sql
-- Substituição cirúrgica num widget específico
-- SEMPRE fazer backup antes
UPDATE wp_postmeta
SET meta_value = JSON_REPLACE(
meta_value,
'$.elements.elements.settings.link.url',
'https://www.novo-destino.com/contacto'
)
WHERE meta_key = '_elementor_data'
AND post_id IN (1042, 1045, 1088);
-- OBRIGATÓRIO após qualquer UPDATE directo:
-- Executar 'wp elementor flush-css --regenerate' via WP-CLI
```
### Regra Crítica: NUNCA wp search-replace
```bash
# ERRADO — corrompe JSON silenciosamente (whitespace of death)
wp search-replace 'http://antigo.com' 'https://novo.com'
# CORRECTO — usa lógica nativa do Elementor
wp elementor replace-urls 'http://antigo.com' 'https://novo.com'
```
### Avaliação do Método
| Critério | Análise |
|----------|---------|
| Complexidade | Alta — parsing JSON, funções MySQL JSON_* |
| Segurança | Muito Baixa — sem validação, um char inválido corrompe a página |
| Usar quando | Auditoria forense, extracção analítica em massa (10.000+ posts) |
---
## 3. REST API Custom Endpoint
Para agentes externos (n8n, Make, Python) manipularem layouts via HTTP.
### Registar Endpoint (functions.php ou plugin MU)
```php
<?php
add_action('rest_api_init', function () {
register_rest_route('agencia-ai/v1', '/atualizar-layout', [
'methods' => 'POST',
'callback' => 'ai_agent_update_elementor_layout',
'permission_callback' => function() {
return current_user_can('edit_pages');
}
]);
});
function ai_agent_update_elementor_layout(WP_REST_Request $request) {
$post_id = absint($request->get_param('post_id'));
$json_data = $request->get_param('elementor_json');
// wp_slash é OBRIGATÓRIO — protege JSON de corrupção de escape
update_post_meta($post_id, '_elementor_data', wp_slash(json_encode($json_data)));
update_post_meta($post_id, '_elementor_edit_mode', 'builder');
// Invalidação de cache OBRIGATÓRIA
delete_post_meta($post_id, '_elementor_css');
if (did_action('elementor/loaded')) {
\Elementor\Plugin::$instance->files_manager->clear_cache();
}
return rest_ensure_response([
'status' => 'success',
'post_id' => $post_id
]);
}
```
### Autenticação
```bash
# Via Application Passwords (WordPress 5.6+)
curl -X POST https://site.pt/wp-json/agencia-ai/v1/atualizar-layout \
-H "Authorization: Basic $(echo -n 'user:app-password' | base64)" \
-H "Content-Type: application/json" \
-d '{"post_id": 2607, "elementor_json": [...]}'
```
### Avaliação do Método
| Critério | Análise |
|----------|---------|
| Complexidade | Média — endpoint PHP + formatar JSON _elementor_data válido |
| Segurança | Alta — usa permissões WordPress + HTTPS |
| Usar quando | Webhooks, n8n/Make, geração de páginas a partir de CRM |
---
## 4. PHP Hooks — Dynamic Tags
Para conteúdo que se actualiza em runtime sem reescrever JSON.
### Registar Dynamic Tag
```php
<?php
// Herdeiro da classe base de Dynamic Tags
class IA_Agente_Metrica_Tag extends \Elementor\Core\DynamicTags\Tag {
public function get_name() { return 'tag-agente-ia'; }
public function get_title() { return 'Relatório Dinâmico IA'; }
public function get_group() { return 'text'; }
public function get_categories() { return [\Elementor\Modules\DynamicTags\Module::TEXT_CATEGORY]; }
// Render lê do Transients API — o agente IA popula externamente
public function render() {
$dados = get_transient('ia_agente_ultima_metrica');
echo $dados ? wp_kses_post($dados) : 'A aguardar sincronização...';
}
}
// Registar
add_action('elementor/dynamic_tags/register', function($manager) {
$manager->register(new IA_Agente_Metrica_Tag());
});
```
### Padrão: Agente IA actualiza Transient
```php
// Agente IA executa isto remotamente (via eval-file ou endpoint)
set_transient('ia_agente_ultima_metrica', '<p>Taxa de conversão: 4.2%</p>', DAY_IN_SECONDS);
```
### Avaliação do Método
| Critério | Análise |
|----------|---------|
| Complexidade | Alta — OOP PHP, Event Loop WordPress, DynamicTags |
| Segurança | Muito Alta — lógica server-side, CI/CD, sem exposição JSON |
| Usar quando | Conteúdo em tempo real (cotações, stock, métricas) — requer Elementor **Pro** |
---
## 5. Site Kits — Estrutura e Geração
### Estrutura ZIP de um Kit
```
kit.zip
├── manifest.json # Índice mestre — mapeamento de todos os ficheiros
├── site-settings.json # Cores globais, tipografia, configurações do tema
├── header-template.json # Template de cabeçalho
├── footer-template.json # Template de rodapé
└── page-template.json # Templates de páginas
```
### manifest.json (estrutura mínima)
```json
{
"templates": [
{
"title": "Header Principal",
"type": "header",
"fileName": "header-template.json"
}
],
"site_settings": {
"title": "Site Settings",
"fileName": "site-settings.json"
}
}
```
### Geração em Python
```python
import json, zipfile, os
kit_settings = {
"version": "0.4",
"title": "Site Settings",
"settings": {
"system_colors": [
{"_id": "primary", "title": "Primária", "color": "#1E40AF"},
{"_id": "secondary", "title": "Secundária", "color": "#10B981"}
],
"system_typography": [
{"_id": "primary", "title": "Principal", "typography_font_family": "Inter"}
]
}
}
manifest = {
"templates": [],
"site_settings": {"title": "Site Settings", "fileName": "site-settings.json"}
}
os.makedirs('/tmp/kit_build', exist_ok=True)
with open('/tmp/kit_build/site-settings.json', 'w') as f:
json.dump(kit_settings, f)
with open('/tmp/kit_build/manifest.json', 'w') as f:
json.dump(manifest, f)
with zipfile.ZipFile('/tmp/agencia-kit.zip', 'w') as z:
z.write('/tmp/kit_build/manifest.json', 'manifest.json')
z.write('/tmp/kit_build/site-settings.json', 'site-settings.json')
# Importar via WP-CLI (CWP)
# /opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp elementor kit import /tmp/agencia-kit.zip \
# --include=site-settings,templates --overrideConditions=all \
# --allow-root --path=/home/USER/public_html
```
---
## 6. CSS — Invalidação de Cache
### Localização dos Ficheiros CSS
```
wp-content/uploads/elementor/css/post-{ID}.css # CSS por página
wp-content/uploads/elementor/css/global.css # CSS global
```
### Invalidação Programática (PHP)
```php
// Após qualquer alteração directa a _elementor_data ou post_meta
if (did_action('elementor/loaded')) {
\Elementor\Plugin::$instance->files_manager->clear_cache();
}
// Alternativa: apagar meta específica
delete_post_meta($post_id, '_elementor_css');
```
### Rebranding Global — Kit Activo
```php
// Obter ID do kit activo
$kit_id = get_option('elementor_active_kit');
// Ler configurações globais
$settings = get_post_meta($kit_id, '_elementor_page_settings', true);
// Alterar cor primária
if (!empty($settings['system_colors'])) {
foreach ($settings['system_colors'] as &$cor) {
if ($cor['_id'] === 'primary') {
$cor['color'] = '#FF0000';
}
}
}
// Guardar (wp_slash protege o JSON)
update_post_meta($kit_id, '_elementor_page_settings', $settings);
// Invalidar cache CSS
if (did_action('elementor/loaded')) {
\Elementor\Plugin::$instance->files_manager->clear_cache();
}
```
---
## 7. Regras Críticas e Riscos
### Nunca fazer
| Erro | Consequência | Correcto |
|------|-------------|----------|
| `wp search-replace` em `_elementor_data` | Corrupção silenciosa do JSON, layout desaparece | `wp elementor replace-urls` |
| UPDATE MySQL sem flush-css | CSS desactualizado, design dessincronizado | Sempre `wp elementor flush-css --regenerate` |
| `update_post_meta` sem `wp_slash()` | Corrupção de escapes no JSON | `wp_slash(json_encode($data))` |
| Múltiplos agentes a editar o mesmo post | Sobreposição de dados | Locks ou filas de processamento |
### IDs de Widgets São Aleatórios
Os IDs de nós JSON (ex: `"id":"3130e2cf"`) são alfanuméricos aleatórios. Para manipular um widget específico via MySQL ou REST API:
1. Fazer parsing da árvore JSON completa primeiro
2. Encontrar o widget pelo `widgetType` ou conteúdo
3. Só então operar no ID correcto
### Limitações Elementor Free vs Pro
| Funcionalidade | Free | Pro |
|----------------|------|-----|
| `wp elementor flush-css` | ✅ | ✅ |
| `wp elementor kit import` | ✅ | ✅ |
| Dynamic Tags | ❌ | ✅ |
| Theme Builder (header/footer) | ❌ | ✅ |
---
## 8. MCP Elementor (Integração Futura)
O projecto **Ultimate Elementor MCP** expõe ~60 ferramentas para manipulação via linguagem natural:
- `create_container`, `create_heading`, `delete_element`
- `get_page_structure` — lê árvore JSON (resolve problema dos IDs aleatórios)
- `update_widget_settings`
**Autenticação:** Application Passwords ou JWT (mesma arquitectura do REST API).
**Quando usar:** Assistentes conversacionais, automações complexas onde formatar JSON raw em Python seria impraticável.
**Referência:** [Ultimate Elementor MCP — LobeHub](https://lobehub.com/mcp/mbrown1837-ultimate-elementor-mcp)
---
*Automação Elementor | Descomplicar® | Baseado em: Manipulação Programática do Elementor — Automação via IA | 18-02-2026*

View File

@@ -0,0 +1,295 @@
---
name: jetengine
description: >
JetEngine development: Custom Post Types (CPT), Custom Content Types (CCT), Meta Boxes, Taxonomies,
Relations, Query Builder, Listing Grid, Listing Map, Dynamic Content, Dynamic Tags, Macros, Profile Builder,
REST API, and headless/programmatic automation for AI agents and n8n integration.
This skill should be used when the user asks about "jetengine", "jet engine", "custom post type jetengine",
"meta box jetengine", "relations jetengine", "query builder jetengine", "listing grid", "listing map",
"jetengine dynamic content", "jetengine macros", "profile builder jetengine", "cct jetengine",
"custom content type", "jetengine cct", "jetengine rest api", "jetengine n8n", "jetengine sql",
"jetengine automação", "jetengine headless", "wp_jet_rel_default", "wp_jet_cct", "jetengine relations",
"conteúdo dinâmico elementor jetengine", "jetengine filtros", "jetengine repeater".
author: Descomplicar® Crescimento Digital
version: 1.0.0
user_invocable: true
tags: [wordpress, elementor, crocoblock, jetengine, cpt, cct, listings, dynamic-content]
allowed-tools: Read, Write, Edit, Bash, mcp__ssh-unified__ssh_execute, mcp__notebooklm__notebook_query, mcp__dify-kb__dify_kb_retrieve_segments
category: dev
quality_score: 85
updated: "2026-02-18T00:00:00Z"
---
# /jetengine — JetEngine Development
JetEngine é o núcleo do ecossistema Crocoblock: cria CPT, CCT, Meta Boxes, Relations, Queries e Listings com conteúdo dinâmico para Elementor.
## CPT vs CCT — Escolha Crítica
| | Custom Post Type (CPT) | Custom Content Type (CCT) |
|--|----------------------|--------------------------|
| **Storage** | `wp_posts` + `wp_postmeta` | Tabela SQL própria `wp_jet_cct_{slug}` |
| **Performance** | Lenta em queries complexas | Muito rápida (colunas reais) |
| **Compatibilidade** | Total WP ecosystem | Limitada (sem taxonomies nativas) |
| **REST API** | `/wp-json/wp/v2/{slug}` | `/wp-json/jet-cct/{slug}` |
| **Automação** | `wp_insert_post()` | `CCT_Data_Handler` ou SQL directo |
| **Usar quando** | Conteúdo editorial, SEO, plugins WP | Alta performance, CRUD automatizado, dashboards |
**Regra prática:** CPT para conteúdo público/SEO; CCT para dados internos, integrações e alta escala.
## Criar CPT
```
JetEngine > Post Types > Add New
├── Slug (único, sem espaços, lowercase)
├── Labels (Singular / Plural)
├── Suporte: title, editor, thumbnail, excerpt, custom-fields
├── REST API: Activar para integração externa
├── Archive: Activar se precisar de página de listagem
└── Admin Columns: Mostrar meta fields na tabela admin
```
## Meta Boxes e Campos
### Tipos de Campos
| Tipo | Uso |
|------|-----|
| `text` | Texto simples |
| `textarea` | Texto longo |
| `wysiwyg` | Editor rich text |
| `number` | Valor numérico |
| `select` | Dropdown de opções |
| `checkbox` | Verdadeiro/falso |
| `checkbox-list` | Múltipla selecção |
| `radio` | Opção única |
| `date` | Data (timestamp) |
| `media` | Upload de ficheiro/imagem |
| `gallery` | Múltiplas imagens |
| `repeater` | Campo repetível (sub-campos) |
| `posts` | Relação simples (seleccionar posts) |
| `color-picker` | Cor HEX |
| `map` | Coordenadas geográficas |
| `html` | HTML estático (apenas display) |
### Repeater — Estrutura
```
Repeater Field "galeria_servicos":
├── sub-campo: imagem (media)
├── sub-campo: titulo (text)
└── sub-campo: descricao (textarea)
Aceder em PHP:
$items = get_post_meta($post_id, 'galeria_servicos', true);
foreach ($items as $item) { ... }
```
## Relations
### Tipos de Relação
| Tipo | Exemplo |
|------|---------|
| One-to-Many | 1 Empresa → N Colaboradores |
| Many-to-Many | N Projectos ↔ N Tags custom |
| Self-relation | Artigo relacionado com outros artigos |
| User-to-Post | Utilizador → N Reservas |
### Tabela SQL das Relações
```sql
-- wp_jet_rel_default
-- parent_object_id | child_object_id | rel_id (hash da relação)
-- Ler todos os filhos de um pai
SELECT child_object_id
FROM wp_jet_rel_default
WHERE parent_object_id = 123
AND rel_id = 'relacao_empresa_colaboradores';
-- Ler o pai a partir de um filho
SELECT parent_object_id
FROM wp_jet_rel_default
WHERE child_object_id = 456
AND rel_id = 'relacao_empresa_colaboradores';
```
**Importante:** Nunca editar esta tabela directamente via SQL para criar relações — usar a API PHP do JetEngine (ver `references/automation.md`).
## Query Builder
Substitui o `WP_Query` com interface visual. Ligar ao Listing Grid.
### Tipos de Query
| Tipo | Usa |
|------|-----|
| Posts | CPT, posts, páginas |
| Terms | Taxonomias |
| Users | Utilizadores WP |
| Custom Content Type | Tabelas CCT |
| REST API | Endpoint externo |
### Configurar Query Posts
```
JetEngine > Query Builder > Add New
├── Query Type: Posts
├── Post Type: [seleccionar CPT]
├── Order By: date / meta_value / rand / menu_order
├── Meta Query: adicionar condições de meta fields
├── Tax Query: filtrar por taxonomia
└── Author: específico ou current user
```
### Usar no Listing Grid
```
Elemento Elementor: Listing Grid
├── Listing Item: [seleccionar Listing Template]
├── Use Custom Query: YES → seleccionar Query criado acima
├── Items per page: N
└── Pagination: Activar se necessário
```
## Listing Grid e Listing Template
### Criar Listing Template
```
JetEngine > Listings > Add New
├── Template Type: Default / Popup
├── Content From: Posts / CCT / User / Term
└── Editar com Elementor → usar Dynamic Tags
```
### Dynamic Tags no Listing Template
```
Post Title → {{ post_title }}
Field de Meta → Meta Field → seleccionar nome do campo
Imagem → Featured Image → meta field media
Link → Post URL / Custom Field URL
Relação → Relation Field
```
### Listing Map
```
JetEngine > Listings > Add New > Template Type: Map
├── Configurar campo de coordenadas (tipo: map no meta box)
├── Google Maps API Key: WP Admin > JetEngine > Settings
└── Ligar ao Query Builder
```
## Dynamic Content e Macros
### Macros Comuns
```
%current_user_id% # ID do utilizador logado
%current_post_id% # ID do post actual
%queried_object_id% # ID do objecto em arquivo/single
%request_field|name% # Parâmetro URL (?name=valor)
%current_meta|field_name% # Meta field do post actual
%user_meta|field_name% # Meta field do utilizador logado
```
### Usar Macros em Widgets Elementor
```
Textos: escrever macro directamente no campo de texto
Links: usar em URL field
Queries: usar em Meta Query → Valor = macro
```
### Profile Builder
```
JetEngine > Profile Builder > Add New
├── Criar páginas de conta (Dashboard, Submissões, etc.)
├── Cada página = Elementor Template
├── Acesso: utilizadores logados, por role, por condição
└── Menu de Profile: widget JetEngine Profile Menu
```
## Dify KB — Consultar Antes de Implementar
```javascript
// Dataset Crocoblock (JetEngine incluído)
mcp__dify-kb__dify_kb_retrieve_segments({
dataset_id: "bdf85c26-1824-4021-92d1-be20501b35ac",
query: "[funcionalidade JetEngine]"
})
```
## Troubleshooting
| Problema | Causa | Solução |
|----------|-------|---------|
| Listing não mostra resultados | Query sem posts ou condição errada | Debug Query: activar `WP_DEBUG` e verificar |
| Dynamic Tag retorna vazio | Campo meta não preenchido ou nome errado | Verificar key do meta field em JetEngine > Meta Boxes |
| Relações não aparecem | rel_id errado ou sem items relacionados | Verificar nome da relação em JetEngine > Relations |
| CCT não aparece no REST | REST API não activada | JetEngine > Content Types > Edit > Enable REST API |
| Filtros não funcionam | Listing ID errado no filtro | JetEngine Listing Grid > copiar ID do widget |
## Bridge Plugin — Deploy para Integração n8n
Para integração headless com n8n ou agentes IA, fazer deploy do MU plugin incluído:
```bash
PHP="/opt/alt/php-fpm83/usr/bin/php"
WP="$PHP /usr/local/bin/wp --allow-root --path=/home/USER/public_html"
# Copiar bridge plugin para mu-plugins (via SSH)
# O ficheiro está em: assets/descomplicar-jet-bridge.php (nesta skill)
scp descomplicar-jet-bridge.php server.descomplicar.pt:/home/USER/public_html/wp-content/mu-plugins/
# Verificar que está activo (MU plugins carregam automaticamente)
$WP plugin list --skip-plugins --skip-themes # MU plugins não aparecem aqui — normal
```
**Endpoint exposto:** `POST /wp-json/descomplicar/v1/manage`
**Acções disponíveis:**
| Acção | Descrição |
|-------|-----------|
| `create_cct_item` | Criar item num CCT |
| `update_cct_item` | Actualizar item (`_ID` em item_data) |
| `get_cct_item` | Ler item por ID |
| `delete_cct_item` | Eliminar item |
| `manage_relation` | Criar/remover relação (connect: true/false) |
```bash
# Testar endpoint (exemplo: criar fatura)
curl -X POST https://site.pt/wp-json/descomplicar/v1/manage \
-H "Authorization: Basic $(echo -n 'user:app-password' | base64)" \
-H "Content-Type: application/json" \
-d '{
"action": "create_cct_item",
"cct_slug": "faturas",
"item_data": { "titulo": "Fatura #001", "valor": 500 }
}'
```
## JetEngine MCP Server (v3.8.0+)
A partir da versão 3.8.0, o JetEngine tem **MCP Server nativo**:
```
WP Admin > JetEngine > AI Command Center > Enable MCP Server
→ Agentes IA podem inspecionar e criar CPTs/CCTs/Relations via linguagem natural
→ Claude consegue ler o schema completo e instanciar estruturas directamente
```
## Referências Adicionais
- **`assets/descomplicar-jet-bridge.php`** — MU Plugin pronto a deployar: CRUD CCT + Relations via REST API
- **`references/automation.md`** — BD completa (CCT, Relations meta, Custom Meta Storage), WP-CLI batch, PHP hooks, REST API avançada (store_items_type, filtros CCT), cache e guardrails
- **`references/query-listings.md`** — Query Builder avançado, Listing Map, paginação, ordenação dinâmica
- **`references/dynamic-content.md`** — Dynamic Tags, Profile Builder, Macros, Conditional Fields
---
**Versão**: 1.1.0 | **Autor**: Descomplicar® | **Data**: 18-02-2026

View File

@@ -0,0 +1,226 @@
<?php
/**
* Plugin Name: Descomplicar JetEngine Bridge
* Description: API Endpoint customizado para manipulação atómica de CCTs e Relações JetEngine.
* Expõe /descomplicar/v1/manage para integração com n8n e agentes IA.
* Version: 1.1.0
* Author: Descomplicar® Crescimento Digital
* Author URI: https://descomplicar.pt
*
* Instalar em: /wp-content/mu-plugins/descomplicar-jet-bridge.php
* Autenticação: WordPress Application Passwords
*/
if ( ! defined( 'ABSPATH' ) ) exit;
class Descomplicar_Jet_Bridge {
public function __construct() {
add_action( 'rest_api_init', [ $this, 'register_routes' ] );
}
public function register_routes() {
register_rest_route( 'descomplicar/v1', '/manage', [
'methods' => 'POST',
'callback' => [ $this, 'handle_request' ],
'permission_callback' => function() {
// Autenticar via WordPress Application Passwords no n8n:
// Header: Authorization: Basic base64(user:app-password)
return current_user_can( 'edit_posts' );
},
]);
}
public function handle_request( WP_REST_Request $request ) {
$params = $request->get_json_params();
$action = sanitize_text_field( $params['action'] ?? '' );
if ( ! function_exists( 'jet_engine' ) || ! class_exists( 'Jet_Engine' ) ) {
return new WP_Error( 'no_jet_engine', 'JetEngine não está activo.', [ 'status' => 500 ] );
}
switch ( $action ) {
case 'create_cct_item':
case 'update_cct_item':
return $this->handle_cct_write( $params );
case 'get_cct_item':
return $this->handle_cct_get( $params );
case 'delete_cct_item':
return $this->handle_cct_delete( $params );
case 'manage_relation':
return $this->handle_relation( $params );
default:
return new WP_Error(
'invalid_action',
'Acção inválida. Disponíveis: create_cct_item, update_cct_item, get_cct_item, delete_cct_item, manage_relation',
[ 'status' => 400 ]
);
}
}
/**
* Criar ou actualizar item CCT
*
* Payload: {
* "action": "create_cct_item",
* "cct_slug": "faturas",
* "item_data": {
* "titulo": "Fatura #1024",
* "valor": 500,
* "estado": "pendente"
* }
* }
* Para update, incluir "_ID": 42 em item_data.
*/
private function handle_cct_write( array $params ) {
$cct_slug = sanitize_key( $params['cct_slug'] ?? '' );
$item_data = $params['item_data'] ?? [];
if ( empty( $cct_slug ) ) {
return new WP_Error( 'missing_slug', 'cct_slug é obrigatório.', [ 'status' => 400 ] );
}
if ( empty( $item_data ) ) {
return new WP_Error( 'missing_data', 'item_data é obrigatório.', [ 'status' => 400 ] );
}
$handler = $this->get_cct_handler( $cct_slug );
if ( is_wp_error( $handler ) ) return $handler;
$result_id = $handler->update_item( $item_data );
if ( ! $result_id ) {
return new WP_Error( 'db_error', 'Falha ao guardar item na base de dados.', [ 'status' => 500 ] );
}
return rest_ensure_response( [
'success' => true,
'item_id' => $result_id,
'action' => isset( $item_data['_ID'] ) ? 'updated' : 'created',
] );
}
/**
* Ler item CCT por ID
*
* Payload: { "action": "get_cct_item", "cct_slug": "faturas", "item_id": 42 }
*/
private function handle_cct_get( array $params ) {
$cct_slug = sanitize_key( $params['cct_slug'] ?? '' );
$item_id = absint( $params['item_id'] ?? 0 );
if ( empty( $cct_slug ) || ! $item_id ) {
return new WP_Error( 'missing_data', 'cct_slug e item_id são obrigatórios.', [ 'status' => 400 ] );
}
$handler = $this->get_cct_handler( $cct_slug );
if ( is_wp_error( $handler ) ) return $handler;
$item = $handler->get_item( $item_id );
if ( ! $item ) {
return new WP_Error( 'not_found', "Item {$item_id} não encontrado no CCT '{$cct_slug}'.", [ 'status' => 404 ] );
}
return rest_ensure_response( [
'success' => true,
'item' => $item,
] );
}
/**
* Eliminar item CCT
*
* Payload: { "action": "delete_cct_item", "cct_slug": "faturas", "item_id": 42 }
*/
private function handle_cct_delete( array $params ) {
$cct_slug = sanitize_key( $params['cct_slug'] ?? '' );
$item_id = absint( $params['item_id'] ?? 0 );
if ( empty( $cct_slug ) || ! $item_id ) {
return new WP_Error( 'missing_data', 'cct_slug e item_id são obrigatórios.', [ 'status' => 400 ] );
}
$handler = $this->get_cct_handler( $cct_slug );
if ( is_wp_error( $handler ) ) return $handler;
$result = $handler->delete_item( $item_id );
return rest_ensure_response( [
'success' => (bool) $result,
'item_id' => $item_id,
'deleted' => (bool) $result,
] );
}
/**
* Criar ou remover relação entre dois items
*
* Payload: {
* "action": "manage_relation",
* "rel_key": "clientes-projetos",
* "parent_id": 10,
* "child_id": 20,
* "connect": true
* }
*/
private function handle_relation( array $params ) {
$rel_key = sanitize_text_field( $params['rel_key'] ?? '' );
$parent_id = absint( $params['parent_id'] ?? 0 );
$child_id = absint( $params['child_id'] ?? 0 );
$connect = (bool) ( $params['connect'] ?? true );
if ( empty( $rel_key ) || ! $parent_id || ! $child_id ) {
return new WP_Error(
'missing_data',
'rel_key, parent_id e child_id são obrigatórios.',
[ 'status' => 400 ]
);
}
if ( ! class_exists( '\Jet_Engine\Relations\Manager' ) ) {
return new WP_Error( 'no_relations', 'Módulo de Relações JetEngine não disponível.', [ 'status' => 500 ] );
}
\Jet_Engine\Relations\Manager::get_instance()->process_relation(
$rel_key,
$parent_id,
$child_id,
$connect
);
return rest_ensure_response( [
'success' => true,
'action' => $connect ? 'connected' : 'disconnected',
'rel_key' => $rel_key,
'parent' => $parent_id,
'child' => $child_id,
] );
}
/**
* Obter CCT handler com verificação de erro
*/
private function get_cct_handler( string $cct_slug ) {
$cct_module = jet_engine()->modules->get_module( 'custom-content-types' );
if ( ! $cct_module ) {
return new WP_Error( 'no_cct_module', 'Módulo CCT não está activo no JetEngine.', [ 'status' => 500 ] );
}
$manager = $cct_module->instance->manager;
$type = $manager->get_content_type_instance( $cct_slug );
if ( ! $type ) {
return new WP_Error( 'invalid_cct', "CCT '{$cct_slug}' não encontrado.", [ 'status' => 404 ] );
}
return $type->get_handler_instance();
}
}
new Descomplicar_Jet_Bridge();

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*

View File

@@ -0,0 +1,295 @@
---
name: rank-math
description: This skill should be used when the user asks to "optimizar SEO wordpress",
"rank math wp-cli", "configurar rank math", "audit rank math", "update meta SEO",
"schema markup wordpress", "redirects rank math", "sitemap wordpress", "bulk SEO update",
"rank math CWP", "titles meta descriptions wp-cli", "configurar sitemap wordpress",
"noindex wordpress", "rank math robots". Manages Rank Math SEO plugin via WP-CLI
on CWP servers.
author: Descomplicar® Crescimento Digital
version: 1.0.0
quality_score: 80
user_invocable: true
---
# /rank-math - Gestão Rank Math SEO via WP-CLI
Operações de SEO WordPress via WP-CLI no servidor CWP (server.descomplicar.pt). Cobre auditoria, configuração em massa, schema markup, redirects e sitemap.
**Manual completo:** `Hub/06-Operacoes/Documentacao/Manuais/Rank-Math-WP-CLI-Manual-Definitivo.md`
**Quick Reference:** `Hub/06-Operacoes/Documentacao/Quick-Reference/QR-RankMath-WP-CLI.md`
**NotebookLM:** [WordPress Config CLI](https://notebooklm.google.com/notebook/fb2f26bd-8cb0-4d4c-bafc-4f1ebb51c51d)
---
## Uso
```
/rank-math audit https://site.pt
/rank-math update-meta /home/USER/public_html
/rank-math sitemap /home/USER/public_html
/rank-math redirects /home/USER/public_html
/rank-math schema post ID /home/USER/public_html
```
---
## Contexto CWP — Sempre Obrigatório
**PHP 8.3 no CWP:**
```bash
# Formato base
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp <comando> --allow-root --path=/home/USER/public_html
# Alias ~/.bashrc (se configurado)
wp83 <comando> --allow-root --path=/home/USER/public_html
```
**Acesso via MCP SSH:**
```
servidor: server.descomplicar.pt (176.9.3.158)
porta: 9443 | user: root
```
**Único comando nativo Rank Math:**
```bash
wp rankmath sitemap generate --allow-root --path=/home/USER/public_html
```
Tudo o resto usa `wp post meta`, `wp option patch`, `wp eval` ou `wp db query`.
---
## Workflow: Auditoria SEO (/rank-math audit)
### Passo 1 — Diagnóstico rápido
```bash
PATH=/home/USER/public_html
# Posts sem título SEO
wp db query "SELECT p.ID, p.post_title FROM wp_posts p
LEFT JOIN wp_postmeta pm ON p.ID=pm.post_id AND pm.meta_key='rank_math_title'
WHERE p.post_type='post' AND p.post_status='publish'
AND (pm.meta_value IS NULL OR pm.meta_value='');" --allow-root --path=$PATH
# Posts sem descrição SEO
wp db query "SELECT p.ID, p.post_title FROM wp_posts p
LEFT JOIN wp_postmeta pm ON p.ID=pm.post_id AND pm.meta_key='rank_math_description'
WHERE p.post_type='post' AND p.post_status='publish'
AND (pm.meta_value IS NULL OR pm.meta_value='');" --allow-root --path=$PATH
# Top 404s (últimos 30 dias)
wp db query "SELECT uri, SUM(times_accessed) as total FROM wp_rank_math_404_logs
GROUP BY uri ORDER BY total DESC LIMIT 20;" --allow-root --path=$PATH
# Tamanho options (detetar bloat WooCommerce)
wp db query "SELECT option_name, LENGTH(option_value) as bytes FROM wp_options
WHERE option_name LIKE 'rank%' AND autoload='yes' ORDER BY bytes DESC;" --allow-root --path=$PATH
```
### Passo 2 — Estado módulos
```bash
wp option pluck rank_math_modules --allow-root --path=$PATH
wp option pluck rank-math-options-general strip_category_base --allow-root --path=$PATH
wp option pluck rank-math-options-titles title_separator --allow-root --path=$PATH
```
---
## Workflow: Actualizar Meta SEO (/rank-math update-meta)
### Meta key individual
```bash
# Título SEO
wp post meta update ID rank_math_title "%title% %sep% %sitename%" --allow-root --path=$PATH
# Descrição SEO (<160 chars)
wp post meta update ID rank_math_description "Texto conciso da página." --allow-root --path=$PATH
# Focus keyword
wp post meta update ID rank_math_focus_keyword "keyword1,keyword2" --allow-root --path=$PATH
# Robots (ARRAY JSON — obrigatório --format=json)
wp post meta update ID rank_math_robots '["noindex"]' --format=json --allow-root --path=$PATH
# Canonical URL
wp post meta update ID rank_math_canonical_url "https://site.pt/url/" --allow-root --path=$PATH
# Pillar content
wp post meta update ID rank_math_pillar_content "on" --allow-root --path=$PATH
```
### Bulk update (wp eval)
```bash
# Aplicar título padrão a todos os posts sem título
wp eval '
$posts = get_posts(["numberposts"=>-1,"post_status"=>"publish"]);
foreach ($posts as $p) {
if (!get_post_meta($p->ID, "rank_math_title", true)) {
update_post_meta($p->ID, "rank_math_title", "%title% %sep% %sitename%");
}
}
echo count($posts)." posts processados\n";
' --allow-root --path=$PATH
```
---
## Workflow: Options Globais (/rank-math options)
**Regra crítica:** SEMPRE usar `wp option patch`, NUNCA `wp option update` em options serializadas.
```bash
# Separador de título
wp option patch update rank-math-options-titles title_separator "-" --allow-root --path=$PATH
# Remover base de categoria do URL
wp option patch update rank-math-options-general strip_category_base "on" --allow-root --path=$PATH
# Verificar sub-key
wp option pluck rank-math-options-titles title_separator --allow-root --path=$PATH
# LER option completa (diagnóstico)
wp option get rank-math-options-general --format=json --allow-root --path=$PATH
```
---
## Workflow: Schema Markup (/rank-math schema)
Schema é armazenado como array PHP em `rank_math_schema_{Type}`. Usar `wp eval` (não JSON directo).
```bash
# Criar schema Article num post
wp eval '
update_post_meta(ID, "rank_math_schema_Article", [
"metadata" => ["title"=>"Article","type"=>"custom","shortcode"=>"s-".uniqid(),"isPrimary"=>"1"],
"@type"=>"Article",
"headline"=>"%seo_title%",
"description"=>"%seo_description%",
"author"=>["@type"=>"Person","name"=>"%name%"],
"datePublished"=>"%date(Y-m-dTH:i:sP)%",
"dateModified"=>"%modified(Y-m-dTH:i:sP)%"
]);
echo "Schema criado\n";
' --user=1 --allow-root --path=$PATH
# Criar schema FAQ
wp eval '
update_post_meta(ID, "rank_math_schema_FAQPage", [
"metadata" => ["title"=>"FAQ","type"=>"custom","shortcode"=>"s-".uniqid(),"isPrimary"=>"1"],
"@type"=>"FAQPage",
"mainEntity"=>[
["@type"=>"Question","name"=>"Pergunta 1","acceptedAnswer"=>["@type"=>"Answer","text"=>"Resposta 1"]],
["@type"=>"Question","name"=>"Pergunta 2","acceptedAnswer"=>["@type"=>"Answer","text"=>"Resposta 2"]]
]
]);
echo "FAQ Schema criado\n";
' --user=1 --allow-root --path=$PATH
# Apagar schema corrompido
wp post meta delete ID rank_math_schema_Article --allow-root --path=$PATH
```
---
## Workflow: Redirects (/rank-math redirects)
```bash
# Criar redirect 301
wp eval '
\RankMath\Redirections\DB::add([
"id"=>"",
"sources"=>[["pattern"=>"/url-antiga/","comparison"=>"exact"]],
"url_to"=>"/url-nova/",
"header_code"=>301,
"status"=>"active",
]);
echo "Redirect criado\n";
' --allow-root --path=$PATH
# Redirect com wildcard
wp eval '
\RankMath\Redirections\DB::add([
"id"=>"",
"sources"=>[["pattern"=>"/blog/","comparison"=>"contain"]],
"url_to"=>"/artigos/",
"header_code"=>301,
"status"=>"active",
]);
echo "Redirect wildcard criado\n";
' --allow-root --path=$PATH
# Apagar redirect por ID
wp eval '\RankMath\Redirections\DB::delete(42); echo "Redirect apagado\n";' --allow-root --path=$PATH
# Limpar logs 404 (após resolver)
wp db query "TRUNCATE TABLE wp_rank_math_404_logs;" --allow-root --path=$PATH
```
---
## Workflow: Sitemap (/rank-math sitemap)
```bash
# Regenerar sitemap
wp rankmath sitemap generate --allow-root --path=$PATH
# Excluir post-type do sitemap
wp option patch update rank-math-options-sitemap pt_attachment_sitemap "off" --allow-root --path=$PATH
# Verificar setting
wp option pluck rank-math-options-sitemap pt_post_sitemap --allow-root --path=$PATH
```
---
## Sequência de Cache (Sempre Após Alterações)
```bash
wp cache flush --allow-root --path=$PATH
wp transient delete --all --allow-root --path=$PATH
wp rewrite flush --allow-root --path=$PATH
wp rankmath sitemap generate --allow-root --path=$PATH
```
> Alterações via `wp db query` não invalidam Redis/Memcached. Sempre executar `wp cache flush`.
---
## Gotchas Críticos
| Problema | Causa | Solução |
|----------|-------|---------|
| Robots não aplicam | Injectado como string, não array | `'["noindex"]' --format=json` |
| Options corrompidas | `wp option update` em option serializada | Sempre `wp option patch` |
| Alteração não visível | Object cache (Redis) em memória | `wp cache flush` |
| Schema quebra editor | JSON inválido em `rank_math_schema_*` | `wp post meta delete ID rank_math_schema_*` |
| Sitemap 404 no Nginx | Faltam rewrite rules | Adicionar regras ao server block |
---
## Variáveis Dinâmicas Frequentes
`%title%` · `%sitename%` · `%sep%` · `%excerpt%` · `%category%` · `%currentyear%` · `%seo_title%` · `%seo_description%` · `%post_thumbnail%` · `%url%` · `%name%`
---
## Recursos Adicionais
### Referências
- **`references/commands.md`** — Referência completa de todos os comandos, meta keys, options e módulos
### Documentação Hub
- **Manual:** `Hub/06-Operacoes/Documentacao/Manuais/Rank-Math-WP-CLI-Manual-Definitivo.md` (17 secções)
- **QR:** `Hub/06-Operacoes/Documentacao/Quick-Reference/QR-RankMath-WP-CLI.md`
- **NotebookLM:** [WordPress Config CLI](https://notebooklm.google.com/notebook/fb2f26bd-8cb0-4d4c-bafc-4f1ebb51c51d) (4 fontes)
---
*Rank Math SEO via WP-CLI | Descomplicar® | v1.0.0 | 18-02-2026*

View File

@@ -0,0 +1,379 @@
# Rank Math WP-CLI — Referência Completa de Comandos
**Manual Hub:** `06-Operacoes/Documentacao/Manuais/Rank-Math-WP-CLI-Manual-Definitivo.md`
---
## Arquitectura de Dados
### Tabelas WordPress Standard
| Tabela | Dados Rank Math |
|--------|-----------------|
| `wp_postmeta` | SEO por post: título, descrição, robots, schema, keywords |
| `wp_termmeta` | SEO por taxonomia (categories, tags) |
| `wp_usermeta` | SEO de páginas author archive |
| `wp_options` | Configurações globais do plugin |
### Tabelas Custom Rank Math
| Tabela | Conteúdo |
|--------|---------|
| `wp_rank_math_redirections` | Regras de redirect (301, 302, 410, etc.) |
| `wp_rank_math_redirections_cache` | Cache das regras |
| `wp_rank_math_404_logs` | Log de 404s com contadores |
| `wp_rank_math_analytics_objects` | Analytics (PRO) |
| `wp_rank_math_analytics_ga` | Google Analytics (PRO) |
| `wp_rank_math_analytics_gsc` | Search Console (PRO) |
---
## Meta Keys Completas (wp_postmeta)
### SEO Básico
| Meta Key | Tipo | Exemplo |
|----------|------|---------|
| `rank_math_title` | string | `%title% %sep% %sitename%` |
| `rank_math_description` | string | Máx 160 chars |
| `rank_math_focus_keyword` | string CSV | `kw1,kw2,kw3` |
| `rank_math_pillar_content` | string | `on` / `off` |
| `rank_math_canonical_url` | string URL | `https://site.pt/url/` |
| `rank_math_seo_score` | int | 0-100 (calculado no editor) |
### Robots e Indexação
| Meta Key | Tipo | Valores |
|----------|------|---------|
| `rank_math_robots` | **array JSON** | `["noindex"]`, `["nofollow"]`, `["noindex","nofollow"]` |
```bash
# CORRECTO
wp post meta update ID rank_math_robots '["noindex"]' --format=json --allow-root
# ERRADO (string, não aplicará)
wp post meta update ID rank_math_robots "noindex" --allow-root
```
### Open Graph / Social
| Meta Key | Tipo | Notas |
|----------|------|-------|
| `rank_math_facebook_image` | string URL | Imagem OG |
| `rank_math_facebook_title` | string | Título Facebook |
| `rank_math_facebook_description` | string | Descrição Facebook |
| `rank_math_twitter_card_type` | string | `summary_large_image` |
| `rank_math_twitter_title` | string | |
| `rank_math_twitter_description` | string | |
| `rank_math_twitter_image` | string URL | |
### Schema
| Meta Key | Tipo | Notas |
|----------|------|-------|
| `rank_math_schema_Article` | serialized PHP array | `wp eval` obrigatório |
| `rank_math_schema_FAQPage` | serialized PHP array | |
| `rank_math_schema_HowTo` | serialized PHP array | |
| `rank_math_schema_Product` | serialized PHP array | WooCommerce |
| `rank_math_schema_Recipe` | serialized PHP array | |
| `rank_math_schema_Review` | serialized PHP array | |
| `rank_math_schema_Event` | serialized PHP array | |
| `rank_math_schema_LocalBusiness` | serialized PHP array | |
| `rank_math_schema_VideoObject` | serialized PHP array | |
### WooCommerce
| Meta Key | Tipo | Notas |
|----------|------|-------|
| `rank_math_primary_category` | string (Term ID) | `"15"` |
| `rank_math_schema_Product` | serialized PHP array | Produto WC |
---
## Options Globais (wp_options)
### Options Principais
| Option Name | Tipo | Conteúdo |
|-------------|------|---------|
| `rank-math-options-general` | serialized array | Config geral |
| `rank-math-options-titles` | serialized array | Títulos e meta por post-type |
| `rank-math-options-sitemap` | serialized array | Configuração sitemap |
| `rank_math_modules` | serialized array | Módulos activos/inactivos |
| `rank_math_schema_types` | serialized array | Tipos schema habilitados |
### Sub-keys rank-math-options-general
```bash
wp option pluck rank-math-options-general strip_category_base # on/off
wp option pluck rank-math-options-general breadcrumbs # on/off
wp option pluck rank-math-options-general knowledgegraph_type # person/company
wp option pluck rank-math-options-general url # URL empresa
wp option pluck rank-math-options-general name # Nome empresa/pessoa
```
### Sub-keys rank-math-options-titles
```bash
wp option pluck rank-math-options-titles title_separator # - / | • …
wp option pluck rank-math-options-titles homepage_title # Título homepage
wp option pluck rank-math-options-titles homepage_description # Desc homepage
wp option pluck rank-math-options-titles pt_post_title # Título padrão posts
wp option pluck rank-math-options-titles pt_page_title # Título padrão pages
wp option pluck rank-math-options-titles pt_product_title # Título padrão products
```
### Sub-keys rank-math-options-sitemap
```bash
wp option pluck rank-math-options-sitemap items_per_page # Default: 200
wp option pluck rank-math-options-sitemap pt_post_sitemap # on/off
wp option pluck rank-math-options-sitemap pt_page_sitemap # on/off
wp option pluck rank-math-options-sitemap pt_product_sitemap # on/off
wp option pluck rank-math-options-sitemap pt_attachment_sitemap # on/off (default: off)
```
---
## Módulos Rank Math
```bash
# Ver todos os módulos
wp option get rank_math_modules --format=json --allow-root
# Activar módulo
wp option patch update rank_math_modules MODULE_NAME "1" --allow-root
# Desactivar módulo
wp option patch update rank_math_modules MODULE_NAME "0" --allow-root
```
| Módulo | Slug | Função |
|--------|------|--------|
| SEO Analysis | `seo-analysis` | Score SEO no editor |
| Link Counter | `link-counter` | Contagem links internos/externos |
| Redirections | `redirections` | Sistema de redirects |
| 404 Monitor | `404-monitor` | Log de 404s |
| Schema | `rich-snippet` | Schema markup |
| Sitemap | `sitemap` | XML Sitemap |
| Instant Indexing | `instant-indexing` | Google Indexing API (PRO) |
| Local SEO | `local-seo` | Schema LocalBusiness |
| WooCommerce | `woocommerce` | SEO WooCommerce |
---
## Leitura de Meta
```bash
# Todos os meta do post
wp post meta list 42 --format=table --allow-root | grep rank_math
# Meta específico
wp post meta get 42 rank_math_title --allow-root
wp post meta get 42 rank_math_description --allow-root
wp post meta get 42 rank_math_robots --allow-root
# Meta de term
wp term meta get 15 rank_math_title --allow-root
wp term meta get 15 rank_math_description --allow-root
```
---
## Operações em Massa
### Script de auditoria completo
```bash
#!/bin/bash
PATH_WP="/home/USER/public_html"
WP="/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp --allow-root --path=$PATH_WP"
echo "=== AUDITORIA RANK MATH SEO ==="
echo "Site: $PATH_WP"
echo "Data: $(date)"
echo -e "\n--- Posts sem título SEO ---"
$WP db query "SELECT p.ID, LEFT(p.post_title,50) as titulo FROM wp_posts p
LEFT JOIN wp_postmeta pm ON p.ID=pm.post_id AND pm.meta_key='rank_math_title'
WHERE p.post_type='post' AND p.post_status='publish'
AND (pm.meta_value IS NULL OR pm.meta_value='')
LIMIT 20;"
echo -e "\n--- Posts sem descrição SEO ---"
$WP db query "SELECT p.ID, LEFT(p.post_title,50) as titulo FROM wp_posts p
LEFT JOIN wp_postmeta pm ON p.ID=pm.post_id AND pm.meta_key='rank_math_description'
WHERE p.post_type='post' AND p.post_status='publish'
AND (pm.meta_value IS NULL OR pm.meta_value='')
LIMIT 20;"
echo -e "\n--- Top 10 404s ---"
$WP db query "SELECT uri, SUM(times_accessed) as total FROM wp_rank_math_404_logs
GROUP BY uri ORDER BY total DESC LIMIT 10;"
echo -e "\n--- Módulos activos ---"
$WP option get rank_math_modules --format=json
echo -e "\n--- Tamanho options ---"
$WP db query "SELECT option_name, LENGTH(option_value) as bytes FROM wp_options
WHERE option_name LIKE 'rank%' AND autoload='yes' ORDER BY bytes DESC;"
echo "=== FIM AUDITORIA ==="
```
### Bulk: Aplicar título padrão a posts sem título
```bash
wp eval '
$posts = get_posts(["post_type"=>"post","numberposts"=>-1,"post_status"=>"publish"]);
$updated = 0;
foreach ($posts as $p) {
if (!get_post_meta($p->ID, "rank_math_title", true)) {
update_post_meta($p->ID, "rank_math_title", "%title% %sep% %sitename%");
$updated++;
}
}
echo "$updated posts actualizados\n";
' --allow-root --path=$PATH_WP
```
### Bulk: Aplicar descrição a partir do excerpt
```bash
wp eval '
$posts = get_posts(["post_type"=>"post","numberposts"=>-1,"post_status"=>"publish"]);
$updated = 0;
foreach ($posts as $p) {
if (!get_post_meta($p->ID, "rank_math_description", true) && $p->post_excerpt) {
$desc = wp_trim_words($p->post_excerpt, 20, "...");
update_post_meta($p->ID, "rank_math_description", $desc);
$updated++;
}
}
echo "$updated posts actualizados\n";
' --allow-root --path=$PATH_WP
```
---
## Schema — Exemplos Completos
### Article (blog post)
```bash
wp eval '
$post_id = ID;
update_post_meta($post_id, "rank_math_schema_Article", [
"metadata" => [
"title"=>"Article",
"type"=>"custom",
"shortcode"=>"s-".uniqid(),
"isPrimary"=>"1"
],
"@type"=>"Article",
"headline"=>"%seo_title%",
"description"=>"%seo_description%",
"url"=>"%url%",
"author"=>["@type"=>"Person","name"=>"%name%"],
"publisher"=>["@type"=>"Organization","name"=>"%sitename%","logo"=>""],
"datePublished"=>"%date(Y-m-dTH:i:sP)%",
"dateModified"=>"%modified(Y-m-dTH:i:sP)%",
"image"=>"%post_thumbnail%"
]);
echo "Article schema criado no post $post_id\n";
' --user=1 --allow-root --path=$PATH_WP
```
### LocalBusiness (página contacto)
```bash
wp eval '
update_post_meta(ID, "rank_math_schema_LocalBusiness", [
"metadata" => ["title"=>"LocalBusiness","type"=>"custom","shortcode"=>"s-".uniqid(),"isPrimary"=>"1"],
"@type"=>"LocalBusiness",
"name"=>"Nome Empresa",
"description"=>"%seo_description%",
"url"=>"%url%",
"email"=>"email@empresa.pt",
"telephone"=>"+351 XXX XXX XXX",
"address"=>["@type"=>"PostalAddress","streetAddress"=>"Rua X","addressLocality"=>"Cidade","postalCode"=>"XXXX-XXX","addressCountry"=>"PT"],
"geo"=>["@type"=>"GeoCoordinates","latitude"=>"38.7","longitude"=>"-9.1"],
"openingHoursSpecification"=>[
["@type"=>"OpeningHoursSpecification","dayOfWeek"=>["Monday","Tuesday","Wednesday","Thursday","Friday"],"opens"=>"09:00","closes"=>"18:00"]
],
"image"=>"%post_thumbnail%"
]);
echo "LocalBusiness schema criado\n";
' --user=1 --allow-root --path=$PATH_WP
```
---
## Instant Indexing (Google Indexing API - PRO)
```bash
# Submeter URL individual
wp eval '\RankMath\Instant_Indexing\Api::submit("https://site.pt/url/", "URL_UPDATED"); echo "OK\n";' --allow-root --path=$PATH_WP
# Verificar estado da API
wp option pluck rank-math-options-instant-indexing bing_api_key --allow-root --path=$PATH_WP
```
---
## Redirects — Referência Completa
### Tipos de Comparação
| comparison | Comportamento |
|-----------|---------------|
| `exact` | Match exacto do URL |
| `contains` | URL contém o pattern |
| `starts_with` | URL começa com o pattern |
| `ends_with` | URL termina com o pattern |
| `regex` | Regular expression |
### Códigos HTTP
| Código | Tipo | Uso |
|--------|------|-----|
| 301 | Moved Permanently | Redirect definitivo |
| 302 | Found | Redirect temporário |
| 307 | Temporary Redirect | Temporário (preserva método HTTP) |
| 410 | Gone | Conteúdo removido permanentemente |
| 451 | Unavailable For Legal | Removido por razões legais |
---
## Tabelas Rank Math — Verificação
```bash
# Ver tabelas custom
wp db query "SHOW TABLES LIKE '%rank_math%';" --allow-root --path=$PATH_WP
# Estrutura tabela redirections
wp db query "DESCRIBE wp_rank_math_redirections;" --allow-root --path=$PATH_WP
# Ver todos os redirects activos
wp db query "SELECT id, sources, url_to, header_code FROM wp_rank_math_redirections WHERE status='active';" --allow-root --path=$PATH_WP
```
---
## Free vs PRO — Funcionalidades WP-CLI
| Funcionalidade | Free | PRO | Notas |
|---------------|------|-----|-------|
| `wp rankmath sitemap generate` | ✅ | ✅ | Único comando nativo |
| Meta por post | ✅ | ✅ | `wp post meta` |
| Options globais | ✅ | ✅ | `wp option patch` |
| Schema markup | ✅ | ✅ | `wp eval` |
| Redirections | ✅ | ✅ | `\RankMath\Redirections\DB` |
| 404 Monitor | ✅ | ✅ | `wp_rank_math_404_logs` |
| Instant Indexing | ❌ | ✅ | `\RankMath\Instant_Indexing\Api` |
| Analytics GSC | ❌ | ✅ | Tabelas analytics_gsc |
| Analytics GA | ❌ | ✅ | Tabelas analytics_ga |
---
*Referência Rank Math WP-CLI | Descomplicar® | v1.0.0 | 18-02-2026*

View File

@@ -0,0 +1,271 @@
---
name: woocommerce-cli
description: This skill should be used when the user asks to "gerir woocommerce linha de
comandos", "woocommerce wp-cli", "criar produto woocommerce cli", "actualizar stock
woocommerce cli", "gerir encomendas woocommerce cli", "importar produtos woocommerce",
"exportar produtos woocommerce", "configurar shipping woocommerce cli", "payment gateway
woocommerce cli", "limpar cache woocommerce cli", "bulk update woocommerce", "woocommerce
cwp", "wc cli cwp", "wp wc product", "wp wc shop_order", "configurar woocommerce servidor",
"scripting woocommerce", "automacao woocommerce cli". WooCommerce management via WP-CLI
on CWP server (server.descomplicar.pt) covering products, orders, customers, shipping,
payments, and automation.
author: Descomplicar® Crescimento Digital
version: 1.0.0
quality_score: 80
user_invocable: true
---
# /woocommerce-cli — WooCommerce via WP-CLI no CWP
Gestão completa de lojas WooCommerce via linha de comandos no servidor CWP. Cobre produtos, encomendas, clientes, configuração, automação e scripting.
**Servidor:** server.descomplicar.pt (176.9.3.158) | porta SSH: 9443
**Manual:** `Hub/06-Operacoes/Documentacao/Manuais/WooCommerce com WP-CLI_ Guia Completo.md`
**Skill relacionada:** `/woocommerce` — desenvolvimento e extensões WooCommerce
---
## Contexto CWP — Regras Obrigatórias
```bash
# 1. SEMPRE prefixar com PHP completo
# 2. SEMPRE --allow-root (CWP executa como root)
# 3. SEMPRE --user=1 em operações de escrita (mapeamento REST API)
# 4. SEMPRE --path= (múltiplos sites no mesmo servidor)
# Formato base
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc <ENTIDADE> <OP> \
--user=1 --allow-root --path=/home/USER/public_html
# Variável de conveniência para scripts
PATH_WP="/home/USER/public_html"
WP="/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp --allow-root --path=$PATH_WP"
$WP wc product list --user=1
```
### Versões PHP disponíveis
| Alias | PHP | Path |
|-------|-----|------|
| `wp74` | 7.4 | `/opt/alt/php-fpm74/usr/bin/php` |
| `wp82` | 8.2 | `/opt/alt/php-fpm82/usr/bin/php` |
| `wp83` | 8.3 | `/opt/alt/php-fpm83/usr/bin/php` (recomendado) |
### Parâmetros essenciais WC CLI
| Parâmetro | Uso |
|-----------|-----|
| `--user=1` | Obrigatório em escrita (autenticação REST API) |
| `--porcelain` | Retorna apenas ID numérico (para scripts) |
| `--format=table\|json\|csv\|ids` | Formato do output |
| `--fields=id,name,price` | Limitar campos (reduz memória) |
| `--per_page=100` | Limite máximo da API (paginar para mais) |
| `--force` | Eliminar definitivamente (sem lixo) |
---
## Produtos
### Operações Comuns
```bash
PATH_WP="/home/USER/public_html"
# Listar produtos
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc product list \
--user=1 --fields=id,name,price,stock_quantity --format=table \
--allow-root --path=$PATH_WP
# Criar produto simples
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc product create \
--user=1 \
--name="Produto Exemplo" \
--type=simple \
--regular_price="29.99" \
--sku="PROD-001" \
--manage_stock=true \
--stock_quantity=100 \
--status=publish \
--allow-root --path=$PATH_WP
# Actualizar preço e stock
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc product update 456 \
--user=1 \
--regular_price="34.99" \
--stock_quantity=50 \
--allow-root --path=$PATH_WP
# Eliminar definitivamente
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc product delete 456 \
--user=1 --force --allow-root --path=$PATH_WP
```
### Produto Variável (capturar ID)
```bash
# 1. Criar produto pai (--porcelain retorna só o ID)
PARENT_ID=$(/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc product create \
--user=1 --name="T-Shirt" --type=variable \
--attributes='[{"name":"Tamanho","variation":true,"visible":true,"options":["S","M","L","XL"]}]' \
--porcelain --allow-root --path=$PATH_WP)
# 2. Criar variação
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc product_variation create $PARENT_ID \
--user=1 \
--regular_price="24.99" \
--sku="TSHIRT-M" \
--attributes='[{"name":"Tamanho","option":"M"}]' \
--manage_stock=true --stock_quantity=30 \
--allow-root --path=$PATH_WP
```
### Actualização em Massa por SKU
```bash
#!/bin/bash
PATH_WP="/home/USER/public_html"
WP="/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp --allow-root --path=$PATH_WP"
IDS=$($WP wc product list --user=1 --sku="TSHIRT-*" --format=ids)
for ID in $IDS; do
$WP wc product update $ID --user=1 --stock_quantity=200 --porcelain
done
```
### Exportar Catálogo
```bash
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc product list \
--user=1 --per_page=5000 --format=csv \
--allow-root --path=$PATH_WP \
> /home/USER/catalogo_$(date +%Y%m%d).csv
```
---
## Encomendas
```bash
PATH_WP="/home/USER/public_html"
# Listar encomendas em processamento
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc shop_order list \
--user=1 --status=processing \
--fields=id,total,date_created \
--allow-root --path=$PATH_WP
# Actualizar estado
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc shop_order update 355 \
--user=1 --status=completed \
--allow-root --path=$PATH_WP
# Adicionar nota ao cliente
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc order_note create 355 \
--user=1 \
--note="Encomenda expedida. Rastreio: PT-99887766." \
--customer_note=true \
--allow-root --path=$PATH_WP
# Conclusão massiva de todas as encomendas em processamento
WP="/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp --allow-root --path=$PATH_WP"
for ID in $($WP wc shop_order list --user=1 --status=processing --format=ids); do
$WP wc shop_order update $ID --user=1 --status=completed --porcelain
done
```
---
## Clientes
```bash
# Listar clientes com métricas
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc customer list \
--user=1 --fields=id,email,orders_count,total_spent \
--format=table --allow-root --path=$PATH_WP
# Criar cliente
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc customer create \
--user=1 \
--email="cliente@exemplo.pt" \
--first_name="João" \
--last_name="Silva" \
--billing='{"address_1":"Rua X","city":"Lisboa","postcode":"1000-001","country":"PT"}' \
--allow-root --path=$PATH_WP
```
---
## Configurações da Loja
```bash
PATH_WP="/home/USER/public_html"
# Listar ferramentas disponíveis
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc tool list \
--user=1 --fields=id,name --allow-root --path=$PATH_WP
# Sequência de limpeza pós-alterações (executar sempre após mudanças)
WP="/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp --allow-root --path=$PATH_WP"
$WP wc tool run clear_transients --user=1 # Limpar transients WC
$WP wc tool run recount_terms --user=1 # Recalcular contadores
$WP wc tool run delete_orphaned_variations --user=1 # Variações órfãs
$WP cache flush # Object cache
$WP rewrite flush # Rewrite rules
# Listar payment gateways
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc payment_gateway list \
--user=1 --fields=id,title,enabled --allow-root --path=$PATH_WP
# Desactivar gateway (emergência)
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc payment_gateway update paypal \
--user=1 --enabled=false --allow-root --path=$PATH_WP
```
---
## Cron do Sistema (CWP)
Desactivar WP-Cron virtual — adicionar em `wp-config.php`:
```php
define('DISABLE_WP_CRON', true);
```
Configurar cron real (`crontab -e` como root):
```bash
* * * * * /opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp cron event run --due-now --quiet --allow-root --path=/home/USER/public_html > /dev/null 2>&1
```
---
## Erros Comuns
| Erro | Causa | Solução |
|------|-------|---------|
| `Error: This does not appear to be a WooCommerce installation` | WooCommerce inactivo | `wp plugin activate woocommerce` |
| `Error: Sorry, you cannot list resources` | Falta `--user=` | Adicionar `--user=1` |
| `PHP Fatal error: memory` | Catálogo grande | Adicionar `-d memory_limit=2G` antes de `/usr/local/bin/wp` |
| `wp wc update` não actualiza BD | Bug conhecido | `wp option update woocommerce_db_version "X.X.X"` manual |
| Wizard de onboarding bloqueia | Sem comando CLI | `wp option update woocommerce_task_list_hidden "yes"` |
---
## Recursos Adicionais
### Referência Completa
- **`references/commands.md`** — Todos os tipos de produto (variável, externo, agrupado, virtual, downloadable), shipping zones completo, tributação, cupões, WP All Import, scripts de automação
### Manual Hub
- **`Hub/06-Operacoes/Documentacao/Manuais/WooCommerce com WP-CLI_ Guia Completo.md`** — Guia completo adaptado para CWP (v2.0)
### Skills Relacionadas
- **`/woocommerce`** — Desenvolvimento WooCommerce (PHP, hooks, extensões)
- **`/wp-cli`** — WP-CLI geral no CWP (core, plugins, temas, BD)
---
*WooCommerce CLI no CWP | Descomplicar® | v1.0.0 | 18-02-2026*

View File

@@ -0,0 +1,621 @@
# WooCommerce WP-CLI — Referência Completa (CWP)
**Manual Hub:** `06-Operacoes/Documentacao/Manuais/WooCommerce com WP-CLI_ Guia Completo.md`
---
## Namespace e Entidades Disponíveis
O WooCommerce CLI opera sob `wp wc`. Todas as operações mapeiam para REST API v3.
| Entidade | Comando Base |
|----------|-------------|
| `customer` | Contas, moradas, métricas |
| `product` | Inventário, preços, stock |
| `product_cat` / `product_tag` | Categorias e tags |
| `product_attribute` | Atributos globais |
| `product_variation` | Variações de produto variável |
| `shop_order` | Encomendas |
| `order_note` | Notas de encomenda |
| `shop_coupon` | Cupões de desconto |
| `setting` | Settings WooCommerce |
| `shipping_zone` | Zonas de envio |
| `shipping_zone_location` | Localizações por zona |
| `shipping_zone_method` | Métodos por zona |
| `payment_gateway` | Gateways de pagamento |
| `tax` | Taxas de imposto |
| `tax_class` | Classes de imposto |
| `tool` | Ferramentas de manutenção |
| `webhook` | Webhooks server-to-server |
---
## Tipos de Produto — Exemplos Completos
### Produto Simples
```bash
PATH_WP="/home/USER/public_html"
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc product create \
--user=1 \
--name="T-Shirt Premium Algodão Orgânico" \
--type=simple \
--regular_price="29.99" \
--sale_price="24.99" \
--sku="TSHIRT-PREM-001" \
--manage_stock=true \
--stock_quantity=150 \
--status=publish \
--description="Descrição completa do produto." \
--short_description="Descrição curta para listagens." \
--allow-root --path=$PATH_WP
```
### Produto Virtual (sem envio)
```bash
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc product create \
--user=1 \
--name="Consulta Online 1h" \
--type=simple \
--virtual=true \
--regular_price="75.00" \
--sku="CONSULT-1H" \
--status=publish \
--allow-root --path=$PATH_WP
```
### Produto Downloadable
```bash
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc product create \
--user=1 \
--name="eBook Guia SEO 2026" \
--type=simple \
--virtual=true \
--downloadable=true \
--regular_price="19.90" \
--sku="EBOOK-SEO-2026" \
--downloads='[{"name":"eBook SEO 2026","file":"https://site.pt/ficheiros/ebook-seo-2026.pdf"}]' \
--download_limit=3 \
--download_expiry=365 \
--allow-root --path=$PATH_WP
```
### Produto Externo/Afiliado
```bash
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc product create \
--user=1 \
--name="Software Cloud Parceiro" \
--type=external \
--regular_price="99.00" \
--product_url="https://parceiro.com/checkout/123" \
--button_text="Comprar na Loja do Parceiro" \
--sku="EXT-SOFT-123" \
--allow-root --path=$PATH_WP
```
### Produto Agrupado (Grouped)
```bash
# 1. Criar produto pai agrupado
GROUP_ID=$(/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc product create \
--user=1 --name="Pack Bundle Completo" --type=grouped --sku="PACK-BUNDLE-001" \
--porcelain --allow-root --path=$PATH_WP)
# 2. Criar filho vinculado ao grupo
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc product create \
--user=1 \
--name="Item A do Pack" \
--type=simple \
--regular_price="15.00" \
--sku="PACK-ITEM-A" \
--parent_id=$GROUP_ID \
--allow-root --path=$PATH_WP
```
### Produto Variável com Variações
```bash
# 1. Criar produto pai variável
PARENT_ID=$(/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc product create \
--user=1 \
--name="Sapatilhas Running" \
--type=variable \
--attributes='[{"name":"Tamanho","variation":true,"visible":true,"options":["39","40","41","42","43"]},{"name":"Cor","variation":true,"visible":true,"options":["Preto","Branco"]}]' \
--porcelain --allow-root --path=$PATH_WP)
# 2. Criar variação (Tamanho 40, Cor Preto)
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc product_variation create $PARENT_ID \
--user=1 \
--regular_price="89.99" \
--sku="RUN-40-PT" \
--attributes='[{"name":"Tamanho","option":"40"},{"name":"Cor","option":"Preto"}]' \
--manage_stock=true \
--stock_quantity=25 \
--allow-root --path=$PATH_WP
```
### Produto com Categorias e Atributos Visíveis
```bash
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc product create \
--user=1 \
--name="Casaco Impermeável Inverno" \
--type=simple \
--regular_price="120.00" \
--categories='[{"id":15},{"id":22}]' \
--attributes='[{"name":"Material","visible":true,"options":["Poliéster","Gore-Tex"]},{"name":"Cor","visible":true,"options":["Preto","Azul Marinho"]}]' \
--allow-root --path=$PATH_WP
```
---
## Gestão de Stock
```bash
PATH_WP="/home/USER/public_html"
# Actualizar stock de produto único
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc product update 456 \
--user=1 \
--manage_stock=true \
--stock_quantity=75 \
--stock_status=instock \
--allow-root --path=$PATH_WP
# Colocar produto fora de stock (sem alterar quantidade)
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc product update 456 \
--user=1 --stock_status=outofstock \
--allow-root --path=$PATH_WP
# Actualizar stock em massa por prefixo SKU
WP="/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp --allow-root --path=$PATH_WP"
for ID in $($WP wc product list --user=1 --sku="TSHIRT-*" --format=ids); do
$WP wc product update $ID --user=1 --stock_quantity=200 --porcelain
done
```
---
## Gestão de Encomendas
### Consulta e Filtros
```bash
PATH_WP="/home/USER/public_html"
# Encomendas por estado
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc shop_order list \
--user=1 --status=processing \
--fields=id,total,currency,date_created \
--format=table \
--allow-root --path=$PATH_WP
# Estados disponíveis: pending, processing, on-hold, completed, cancelled, refunded, failed
```
### Actualizar Estado
```bash
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc shop_order update 355 \
--user=1 --status=completed \
--allow-root --path=$PATH_WP
```
### Adicionar Nota (Visível ao Cliente)
```bash
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc order_note create 355 \
--user=1 \
--note="Expedido via CTT. Rastreio: PT-99887766." \
--customer_note=true \
--allow-root --path=$PATH_WP
```
### Conclusão Massiva com Relatório
```bash
#!/bin/bash
PATH_WP="/home/USER/public_html"
WP="/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp --allow-root --path=$PATH_WP"
ORDER_IDS=$($WP wc shop_order list --user=1 --status=processing --format=ids)
TOTAL=0
for ID in $ORDER_IDS; do
$WP wc shop_order update $ID --user=1 --status=completed --porcelain
TOTAL=$((TOTAL+1))
echo "[OK] Encomenda #$ID concluída."
done
echo "Total processado: $TOTAL encomendas."
```
---
## Clientes
```bash
PATH_WP="/home/USER/public_html"
# Listar com métricas de lifetime value
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc customer list \
--user=1 \
--role=customer \
--fields=id,email,orders_count,total_spent \
--format=table \
--allow-root --path=$PATH_WP
# Criar cliente completo (B2B)
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc customer create \
--user=1 \
--email="empresa@exemplo.pt" \
--first_name="Carlos" \
--last_name="Mendes" \
--username="carlosmendes_b2b" \
--password="SenhaForte!2026" \
--billing='{"first_name":"Carlos","last_name":"Mendes","company":"Empresa Lda","address_1":"Avenida da Liberdade 123","city":"Lisboa","postcode":"1250-140","country":"PT"}' \
--shipping='{"first_name":"Carlos","last_name":"Mendes","company":"Armazém Empresa","address_1":"Rua da Indústria 45","city":"Sintra","postcode":"2710-000","country":"PT"}' \
--allow-root --path=$PATH_WP
```
---
## Shipping Zones — Configuração Completa
```bash
PATH_WP="/home/USER/public_html"
# 1. Criar zona geográfica
ZONE_ID=$(/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc shipping_zone create \
--user=1 --name="Portugal Continental" \
--porcelain --allow-root --path=$PATH_WP)
# 2. Adicionar países à zona
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc shipping_zone_location create $ZONE_ID \
--user=1 --code="PT" --type=country \
--allow-root --path=$PATH_WP
# 3. Adicionar método de portes fixos
METHOD_ID=$(/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc shipping_zone_method create $ZONE_ID \
--user=1 --method_id=flat_rate \
--porcelain --allow-root --path=$PATH_WP)
# 4. Definir custo
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc shipping_zone_method update $ZONE_ID $METHOD_ID \
--user=1 --settings='{"cost":"3.99","title":"Portes Padrão"}' \
--allow-root --path=$PATH_WP
# 5. Adicionar envio gratuito acima de X€
FREE_ID=$(/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc shipping_zone_method create $ZONE_ID \
--user=1 --method_id=free_shipping \
--porcelain --allow-root --path=$PATH_WP)
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc shipping_zone_method update $ZONE_ID $FREE_ID \
--user=1 --settings='{"requires":"min_amount","min_amount":"50"}' \
--allow-root --path=$PATH_WP
# Listar todas as zonas
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc shipping_zone list \
--user=1 --format=table --allow-root --path=$PATH_WP
```
---
## Payment Gateways
```bash
PATH_WP="/home/USER/public_html"
# Listar todos os gateways
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc payment_gateway list \
--user=1 --fields=id,title,enabled --allow-root --path=$PATH_WP
# Activar gateway
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc payment_gateway update bacs \
--user=1 --enabled=true --allow-root --path=$PATH_WP
# Desactivar gateway (emergência)
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc payment_gateway update paypal \
--user=1 --enabled=false --allow-root --path=$PATH_WP
# Configurar Stripe em modo teste
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc payment_gateway update stripe \
--user=1 \
--settings='{"testmode":"yes","test_publishable_key":"pk_test_XXXX","test_secret_key":"sk_test_XXXX"}' \
--allow-root --path=$PATH_WP
```
---
## Tributação
```bash
PATH_WP="/home/USER/public_html"
# Criar taxa IVA 23% (Portugal)
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc tax create \
--user=1 \
--country=PT \
--rate="23" \
--name="IVA 23%" \
--class=standard \
--allow-root --path=$PATH_WP
# Criar taxa reduzida 6%
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc tax create \
--user=1 --country=PT --rate="6" --name="IVA 6%" --class=reduced-rate \
--allow-root --path=$PATH_WP
# Criar classe de taxa personalizada
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc tax_class create \
--user=1 --name="Taxa Super Reduzida" \
--allow-root --path=$PATH_WP
# Activar cálculo de impostos
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp option update woocommerce_calc_taxes "yes" \
--allow-root --path=$PATH_WP
```
---
## Cupões
```bash
PATH_WP="/home/USER/public_html"
# Criar cupão de desconto percentual
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc shop_coupon create \
--user=1 \
--code="VERAO2026" \
--discount_type=percent \
--amount="15" \
--usage_limit=500 \
--expiry_date="2026-09-30" \
--allow-root --path=$PATH_WP
# Criar cupão de desconto fixo
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc shop_coupon create \
--user=1 \
--code="DESCONTO10" \
--discount_type=fixed_cart \
--amount="10" \
--minimum_amount="50" \
--allow-root --path=$PATH_WP
# Listar cupões activos
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc shop_coupon list \
--user=1 --fields=id,code,amount,discount_type,usage_count \
--format=table --allow-root --path=$PATH_WP
```
---
## Ferramentas de Manutenção
```bash
PATH_WP="/home/USER/public_html"
WP="/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp --allow-root --path=$PATH_WP"
# Listar todas as ferramentas
$WP wc tool list --user=1 --fields=id,name,description --format=table
# Limpar transients
$WP wc tool run clear_transients --user=1
# Recalcular contadores de taxonomias (categorias/tags)
$WP wc tool run recount_terms --user=1
# Eliminar variações órfãs
$WP wc tool run delete_orphaned_variations --user=1
# Limpar sessões e carrinhos abandonados
$WP wc tool run clear_sessions --user=1
# Regenerar imagens de produto
$WP wc tool run regenerate_thumbnails --user=1
# Regenerar dados de lookup (tabela de relatórios)
$WP wc tool run clear_lookup_table --user=1
# Sequência completa de limpeza (recomendada após alterações massivas)
$WP wc tool run delete_orphaned_variations --user=1
$WP wc tool run recount_terms --user=1
$WP wc tool run clear_sessions --user=1
$WP wc tool run clear_transients --user=1
$WP cache flush
$WP rewrite flush
echo "Limpeza concluída."
```
---
## WP All Import — Importação Massiva
```bash
PATH_WP="/home/USER/public_html"
# Verificar se plugin está activo
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp plugin is-active wp-all-import-pro \
--allow-root --path=$PATH_WP && echo "Activo" || echo "Inactivo"
# Executar perfil de importação pré-configurado (ID=5)
# Configurar mapeamentos no painel WP All Import primeiro
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp all-import run 5 \
--force-run --disable-log \
--allow-root --path=$PATH_WP
# Listar perfis de importação
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp all-import status \
--allow-root --path=$PATH_WP
```
---
## Importação via CSV (Script)
```bash
#!/bin/bash
# Importar produtos a partir de CSV (colunas: SKU,TITULO,PRECO)
PATH_WP="/home/USER/public_html"
WP="/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp --allow-root --path=$PATH_WP"
CSV="/home/USER/produtos.csv"
ERROS=0
OK=0
tail -n +2 "$CSV" | while IFS=, read -r SKU TITULO PRECO; do
ID=$($WP wc product create \
--user=1 \
--sku="$SKU" \
--name="$TITULO" \
--regular_price="$PRECO" \
--type=simple \
--status=publish \
--porcelain 2>/dev/null)
if [ -n "$ID" ] && [ "$ID" -gt 0 ] 2>/dev/null; then
echo "[OK] $SKU → ID $ID"
OK=$((OK+1))
else
echo "[ERRO] $SKU — falhou"
ERROS=$((ERROS+1))
fi
done
echo "Importação concluída. OK: $OK | Erros: $ERROS"
```
---
## Scripts de Manutenção
### Normalização Preventiva de Dados
```bash
#!/bin/bash
PATH_WP="/home/USER/public_html"
WP="/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp --allow-root --path=$PATH_WP"
echo "=== LIMPEZA WOOCOMMERCE ==="
echo "Site: $PATH_WP | $(date)"
$WP wc tool run delete_orphaned_variations --user=1 --quiet && echo "[OK] Variações órfãs"
$WP wc tool run recount_terms --user=1 --quiet && echo "[OK] Contadores taxonomias"
$WP wc tool run clear_sessions --user=1 --quiet && echo "[OK] Sessões"
$WP wc tool run clear_transients --user=1 --quiet && echo "[OK] Transients"
$WP cache flush && echo "[OK] Object cache"
$WP rewrite flush && echo "[OK] Rewrite rules"
$WP db optimize --quiet && echo "[OK] BD optimizada"
echo "=== CONCLUÍDO ==="
```
### Manutenção WooCommerce em Todos os Sites CWP
```bash
#!/bin/bash
find /home -name wp-config.php -not -path "*/backup*" 2>/dev/null | while read config; do
dir=$(dirname "$config")
user=$(stat -c '%U' "$dir")
php_fpm=$(ps aux | grep "php-fpm: pool ${user}" | grep -v grep | head -1 | grep -o 'php-fpm[0-9][0-9]' | head -1)
php_ver=${php_fpm:-php-fpm82}
php_bin="/opt/alt/${php_ver}/usr/bin/php"
WP="$php_bin /usr/local/bin/wp --allow-root --path=$dir"
echo "=== $user ($dir) ==="
# Verificar se WooCommerce está activo
if $WP plugin is-active woocommerce 2>/dev/null; then
$WP wc tool run clear_transients --user=1 --quiet 2>/dev/null
$WP wc tool run recount_terms --user=1 --quiet 2>/dev/null
echo " [WC] Limpeza WooCommerce concluída."
else
echo " [--] WooCommerce não activo."
fi
done
```
---
## Limitações e Bugs Conhecidos
### Memory Exhaustion — Override CWP
```bash
# -d memory_limit antes de /usr/local/bin/wp
/opt/alt/php-fpm83/usr/bin/php -d memory_limit=2G /usr/local/bin/wp wc product list \
--user=1 --per_page=100 --format=ids \
--allow-root --path=/home/USER/public_html
# Para catálogos muito grandes: iterar com --per_page=100 e --page=N
for PAGE in 1 2 3 4 5; do
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp wc product list \
--user=1 --per_page=100 --page=$PAGE --format=ids \
--allow-root --path=/home/USER/public_html
done
```
### Bug `wp wc update` — Versão BD não actualiza
```bash
# Verificar versão instalada
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp plugin get woocommerce \
--field=version --allow-root --path=/home/USER/public_html
# Forçar actualização manual do registo BD
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp option update woocommerce_db_version "9.X.X" \
--allow-root --path=/home/USER/public_html
```
### Bypass Wizard de Onboarding
```bash
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp option update woocommerce_onboarding_opt_in "yes" \
--allow-root --path=/home/USER/public_html
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp option update woocommerce_task_list_hidden "yes" \
--allow-root --path=/home/USER/public_html
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp option update woocommerce_setup_wizard_data \
'{"setup_wizard":"completed"}' --allow-root --path=/home/USER/public_html
```
---
## Settings via wp_options
Algumas configurações WooCommerce não têm endpoint CLI nativo. Usar `wp option update` directamente:
```bash
PATH_WP="/home/USER/public_html"
# Moeda
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp option update woocommerce_currency "EUR" \
--allow-root --path=$PATH_WP
# Posição do símbolo de moeda
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp option update woocommerce_currency_pos "right_space" \
--allow-root --path=$PATH_WP
# Separadores de preço (PT-PT)
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp option update woocommerce_price_thousand_sep "." \
--allow-root --path=$PATH_WP
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp option update woocommerce_price_decimal_sep "," \
--allow-root --path=$PATH_WP
# País de base da loja
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp option update woocommerce_default_country "PT" \
--allow-root --path=$PATH_WP
# Activar impostos
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp option update woocommerce_calc_taxes "yes" \
--allow-root --path=$PATH_WP
```
---
*Referência WooCommerce CLI CWP | Descomplicar® | v1.0.0 | 18-02-2026*

View File

@@ -9,7 +9,7 @@ version: 1.1.0
user_invocable: true
tags: [wordpress, woocommerce, ecommerce, checkout, payments, multibanco]
desk_task: 1479
allowed-tools: Read, Write, Edit, Bash, mcp__ssh-unified__ssh_execute, mcp__dify-kb__dify_kb_retrieve_segments
allowed-tools: Read, Write, Edit, Bash, mcp__ssh-unified__ssh_execute, mcp__notebooklm__notebook_query, mcp__dify-kb__dify_kb_retrieve_segments
category: dev
quality_score: 75
updated: "2026-02-04T18:00:00Z"
@@ -235,6 +235,10 @@ add_filter('woocommerce_product_query', function($q) {
### Como Consultar
```javascript
# PRIMARIO: NotebookLM (Gemini 2.5 RAG)
# mcp__notebooklm__notebook_query({notebook_id: "5be0d1a6-00f2-4cd9-b835-978cb7721601", query: "<tema>"}) // WordPress e Elementor
# FALLBACK: Dify KB (se NotebookLM insuficiente)
// Hooks e customizações WooCommerce
mcp__dify-kb__dify_kb_retrieve_segments({
dataset_id: "9da0b2b9-5051-4b99-b9f6-20bf35067092",

View File

@@ -0,0 +1,332 @@
---
name: wp-cli
description: This skill should be used when the user asks to "wp-cli cwp", "gerir
wordpress linha de comandos", "actualizar plugins wp-cli", "backup wordpress cli",
"migrar site wordpress", "search replace wordpress", "criar utilizador wordpress cli",
"wp-cli no servidor", "wp-cli allow-root", "comandos wordpress server",
"actualizar wordpress servidor", "modo manutencao wordpress", "wp db export",
"wp plugin update server", "wp core update cwp". WP-CLI management on CWP server
(server.descomplicar.pt) with multi-PHP support.
author: Descomplicar® Crescimento Digital
version: 1.0.0
quality_score: 80
user_invocable: true
---
# /wp-cli - Gestão WordPress via WP-CLI no CWP
Operações WordPress via linha de comandos no servidor CWP (server.descomplicar.pt). Cobre core, plugins, temas, base de dados, utilizadores, migração e automação.
**Servidor:** server.descomplicar.pt (176.9.3.158) | porta SSH: 9443
**Fonte:** `Hub/06-Operacoes/Documentacao/Manuais/WP-CLI no CWP.md`
**NotebookLM:** [WordPress Config CLI](https://notebooklm.google.com/notebook/fb2f26bd-8cb0-4d4c-bafc-4f1ebb51c51d)
---
## Contexto CWP — Regras Obrigatórias
```bash
# 1. Usar sempre --allow-root (CWP executa como root)
# 2. Especificar sempre --path= (não fazer cd)
# 3. Usar o PHP correcto para cada utilizador
# Formato base
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp COMANDO --allow-root --path=/home/USER/public_html
# Aliases ~/.bashrc (se configurados)
alias wp74='/opt/alt/php-fpm74/usr/bin/php /usr/local/bin/wp --allow-root'
alias wp82='/opt/alt/php-fpm82/usr/bin/php /usr/local/bin/wp --allow-root'
alias wp83='/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp --allow-root'
```
### Versões PHP disponíveis
| Alias | PHP | Path |
|-------|-----|------|
| `wp74` | 7.4 | `/opt/alt/php-fpm74/usr/bin/php` |
| `wp80` | 8.0 | `/opt/alt/php-fpm80/usr/bin/php` |
| `wp81` | 8.1 | `/opt/alt/php-fpm81/usr/bin/php` |
| `wp82` | 8.2 | `/opt/alt/php-fpm82/usr/bin/php` |
| `wp83` | 8.3 | `/opt/alt/php-fpm83/usr/bin/php` |
### Detectar PHP activo de um utilizador
```bash
# Ver pool PHP-FPM do utilizador
ps aux | grep "php-fpm: pool USER" | grep -v grep | head -1
# Ou usar wp-auto.sh (auto-detect)
/root/wp-auto.sh --path=/home/USER/public_html plugin list
```
O script `wp-auto.sh` está em `scripts/wp-auto.sh` desta skill.
---
## Core WordPress
```bash
PATH=/home/USER/public_html
# Verificar versão
wp core version --allow-root --path=$PATH
# Actualizar WordPress
wp core update --allow-root --path=$PATH
# Verificar integridade dos ficheiros
wp core verify-checksums --allow-root --path=$PATH
# Instalar versão específica
wp core download --version=6.4.3 --force --allow-root --path=$PATH
# Informações ambiente
wp --info --allow-root --path=$PATH
```
---
## Plugins
```bash
# Listar todos os plugins
wp plugin list --allow-root --path=$PATH
# Listar plugins desactualizados
wp plugin list --update=available --format=table --allow-root --path=$PATH
# Actualizar todos os plugins
wp plugin update --all --allow-root --path=$PATH
# Instalar e activar
wp plugin install akismet --activate --allow-root --path=$PATH
# Activar / desactivar
wp plugin activate NOME --allow-root --path=$PATH
wp plugin deactivate NOME --allow-root --path=$PATH
# Remover plugin
wp plugin delete NOME --allow-root --path=$PATH
# Verificar integridade
wp plugin verify-checksums --all --allow-root --path=$PATH
```
---
## Temas
```bash
# Listar temas
wp theme list --allow-root --path=$PATH
# Instalar e activar
wp theme install hello-elementor --activate --allow-root --path=$PATH
# Actualizar todos os temas
wp theme update --all --allow-root --path=$PATH
# Activar tema
wp theme activate NOME --allow-root --path=$PATH
```
---
## Base de Dados
```bash
# Exportar (backup)
wp db export /home/USER/backup_$(date +%Y%m%d_%H%M).sql --allow-root --path=$PATH
# Importar (restaurar)
wp db import backup.sql --allow-root --path=$PATH
# Optimizar tabelas
wp db optimize --allow-root --path=$PATH
# Reparar tabelas corrompidas
wp db repair --allow-root --path=$PATH
# Query directa
wp db query "SELECT option_name, option_value FROM wp_options WHERE option_name='siteurl';" --allow-root --path=$PATH
# Tamanho tabelas
wp db query "SELECT table_name, ROUND((data_length+index_length)/1024/1024,2) AS MB FROM information_schema.tables WHERE table_schema=DATABASE() ORDER BY MB DESC LIMIT 20;" --allow-root --path=$PATH
```
---
## Migração de Site (Search-Replace)
```bash
# Preview (dry-run) — SEMPRE primeiro
wp search-replace 'https://old.com' 'https://new.com' --dry-run --allow-root --path=$PATH
# Executar após confirmar
wp search-replace 'https://old.com' 'https://new.com' --allow-root --path=$PATH
# Com relatório
wp search-replace 'old.com' 'new.com' --report --allow-root --path=$PATH
# Após migração: limpar cache e regenerar rewrite rules
wp cache flush --allow-root --path=$PATH
wp rewrite flush --allow-root --path=$PATH
```
---
## Utilizadores
```bash
# Listar utilizadores
wp user list --allow-root --path=$PATH
# Criar administrador
wp user create admin admin@site.pt --role=administrator --user_pass=SENHA --allow-root --path=$PATH
# Redefinir password
wp user update USERNAME --user_pass=NOVA_SENHA --allow-root --path=$PATH
# Adicionar papel a utilizador existente
wp user set-role USERNAME administrator --allow-root --path=$PATH
# Remover utilizador (transferindo conteúdo)
wp user delete USERNAME --reassign=1 --allow-root --path=$PATH
# Actualizar email admin
wp option update admin_email "novo@email.pt" --allow-root --path=$PATH
```
---
## Cache e Transientes
```bash
# Limpar object cache (Redis/Memcached)
wp cache flush --allow-root --path=$PATH
# Apagar todos os transientes
wp transient delete --all --allow-root --path=$PATH
# Regenerar rewrite rules
wp rewrite flush --allow-root --path=$PATH
# Sequência completa pós-alteração
wp cache flush --allow-root --path=$PATH && \
wp transient delete --all --allow-root --path=$PATH && \
wp rewrite flush --allow-root --path=$PATH
```
---
## Modo Manutenção
```bash
# Activar modo manutenção
wp maintenance-mode activate --allow-root --path=$PATH
# Verificar estado
wp maintenance-mode status --allow-root --path=$PATH
# Desactivar
wp maintenance-mode deactivate --allow-root --path=$PATH
```
---
## Cron WordPress
```bash
# Listar eventos cron
wp cron event list --allow-root --path=$PATH
# Executar evento manualmente
wp cron event run HOOK_NAME --allow-root --path=$PATH
# Ver schedules disponíveis
wp cron schedule list --allow-root --path=$PATH
# Apagar evento
wp cron event delete HOOK_NAME --allow-root --path=$PATH
```
---
## Operações em Massa (Todos os Sites)
```bash
# Encontrar todas as instalações WordPress
find /home -name wp-config.php -not -path "*/backup*" 2>/dev/null | while read config; do
dir=$(dirname "$config")
user=$(stat -c '%U' "$dir")
echo "=== $user: $dir ==="
done
# Actualizar todos os sites (com PHP auto-detect)
find /home -name wp-config.php -not -path "*/backup*" 2>/dev/null | while read config; do
dir=$(dirname "$config")
user=$(stat -c '%U' "$dir")
echo "--- Actualizando $user ---"
php_fpm=$(ps aux | grep "php-fpm: pool ${user}" | grep -v grep | head -1 | grep -o 'php-fpm[0-9][0-9]' | head -1)
php_ver=${php_fpm:-php-fpm82}
/opt/alt/${php_ver}/usr/bin/php /usr/local/bin/wp core update --allow-root --path="$dir" 2>/dev/null
/opt/alt/${php_ver}/usr/bin/php /usr/local/bin/wp plugin update --all --allow-root --path="$dir" 2>/dev/null
done
```
---
## Erros Comuns e Soluções
| Erro | Causa | Solução |
|------|-------|---------|
| `Operation not permitted` | Sem `--allow-root` | Adicionar `--allow-root` |
| `PHP Fatal error: memory` | Limite memória | `define('WP_MEMORY_LIMIT','256M')` no wp-config.php |
| `max_execution_time exceeded` | Timeout em ops longas | `--exec-time=300` ou php.ini |
| `Error establishing database connection` | BD em baixo | `wp db check --allow-root` |
| `Warning: Could not get lock` | Outro processo activo | `wp cli cache clear --allow-root` |
| PHP version wrong | Site usa PHP diferente | `ps aux \| grep "pool USER"` para detectar |
---
## Diagnóstico Rápido
```bash
# Informações completas do ambiente
wp --info --allow-root --path=$PATH
# Verificar conectividade BD
wp db check --allow-root --path=$PATH
# Ver configuração PHP activa
wp eval 'echo PHP_VERSION."\n".php_ini_loaded_file()."\n".ini_get("memory_limit")."\n";' --allow-root --path=$PATH
# Ver URL e email admin
wp option get siteurl --allow-root --path=$PATH
wp option get admin_email --allow-root --path=$PATH
# Listar utilizadores admin
wp user list --role=administrator --allow-root --path=$PATH
```
---
## Recursos Adicionais
### Scripts
- **`scripts/wp-auto.sh`** — Auto-detecta PHP do utilizador e executa WP-CLI com a versão correcta
### Referências
- **`references/commands.md`** — Referência completa: todos os comandos com flags, WooCommerce CLI, options, wp eval avançado
### Documentação Hub
- **Manual:** `Hub/06-Operacoes/Documentacao/Manuais/WP-CLI no CWP.md`
- **NotebookLM:** [WordPress Config CLI](https://notebooklm.google.com/notebook/fb2f26bd-8cb0-4d4c-bafc-4f1ebb51c51d)
- **Skill relacionada:** `/rank-math` — operações Rank Math SEO via WP-CLI
---
*WP-CLI no CWP | Descomplicar® | v1.0.0 | 18-02-2026*

View File

@@ -0,0 +1,376 @@
# WP-CLI no CWP — Referência Completa de Comandos
**Servidor:** server.descomplicar.pt (176.9.3.158) | porta 9443
**PHP padrão:** `/opt/alt/php-fpm83/usr/bin/php`
---
## Formato Base
```bash
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp GRUPO SUBCOMANDO [FLAGS] --allow-root --path=/home/USER/public_html
```
---
## Core WordPress
```bash
wp core version # Versão actual
wp core update # Actualizar para latest
wp core update --version=6.4.3 --force # Versão específica
wp core download # Descarregar sem instalar
wp core verify-checksums # Verificar integridade ficheiros
wp core install --url=... --title=... \
--admin_user=admin --admin_email=... # Instalar novo WordPress
wp core is-installed # Verificar se está instalado
wp core update-db # Actualizar base de dados após update
```
---
## Plugins
```bash
wp plugin list # Listar todos
wp plugin list --status=inactive # Só inactivos
wp plugin list --update=available # Com updates disponíveis
wp plugin install SLUG # Instalar
wp plugin install SLUG --activate # Instalar e activar
wp plugin install SLUG --version=X.X # Versão específica
wp plugin activate SLUG # Activar
wp plugin deactivate SLUG # Desactivar
wp plugin deactivate --all # Desactivar todos (debug)
wp plugin delete SLUG # Remover ficheiros
wp plugin update SLUG # Actualizar plugin específico
wp plugin update --all # Actualizar todos
wp plugin update --all --dry-run # Preview de updates
wp plugin verify-checksums SLUG # Verificar integridade
wp plugin verify-checksums --all # Verificar todos
wp plugin search TERMO # Pesquisar no repo WordPress.org
wp plugin get SLUG # Info detalhada
wp plugin path SLUG # Path do plugin no servidor
```
---
## Temas
```bash
wp theme list # Listar todos
wp theme install SLUG # Instalar
wp theme install SLUG --activate # Instalar e activar
wp theme activate SLUG # Activar tema
wp theme update SLUG # Actualizar
wp theme update --all # Actualizar todos
wp theme delete SLUG # Remover
wp theme get SLUG # Info detalhada
wp theme is-active SLUG # Verificar se activo
```
---
## Base de Dados
```bash
wp db export /caminho/backup.sql # Exportar
wp db export - | gzip > backup.sql.gz # Exportar comprimido
wp db import backup.sql # Importar
wp db optimize # Optimizar tabelas
wp db repair # Reparar tabelas
wp db check # Verificar integridade
wp db size # Tamanho da BD
wp db tables # Listar tabelas
wp db prefix # Prefixo actual
wp db query "SQL;" # Query directa
wp db search "TERMO" # Pesquisar em toda a BD
wp db clean --tables=revision # Limpar revisões
```
---
## Search-Replace (Migração)
```bash
# SEMPRE fazer dry-run primeiro
wp search-replace 'old.com' 'new.com' --dry-run
# Executar
wp search-replace 'https://old.com' 'https://new.com'
# Só numa tabela
wp search-replace 'old' 'new' wp_options wp_postmeta
# Com relatório detalhado
wp search-replace 'old' 'new' --report-changed-only
# Sem serialization (mais rápido, menos seguro)
wp search-replace 'old' 'new' --no-php
# Número de linhas por batch (para BDs grandes)
wp search-replace 'old' 'new' --precise
# Skip tabelas específicas
wp search-replace 'old' 'new' --skip-tables=wp_users
```
---
## Options WordPress
```bash
wp option get NOME # Ler option
wp option update NOME VALOR # Actualizar (simples)
wp option patch update NOME SUBKEY VALOR # Actualizar sub-key (serialized)
wp option pluck NOME SUBKEY # Ler sub-key
wp option add NOME VALOR # Adicionar
wp option delete NOME # Apagar
wp option list # Listar todas
wp option list --search='rank*' # Filtrar por nome
wp option list --autoload=yes # Só com autoload
```
---
## Post Meta / User Meta / Term Meta
```bash
# Post Meta
wp post meta list POST_ID # Listar meta de um post
wp post meta get POST_ID META_KEY # Ler valor
wp post meta update POST_ID META_KEY VAL # Actualizar
wp post meta add POST_ID META_KEY VAL # Adicionar
wp post meta delete POST_ID META_KEY # Apagar
# Com formato JSON (para arrays/objects)
wp post meta update POST_ID META_KEY '["val1","val2"]' --format=json
# Term Meta
wp term meta list TERM_ID
wp term meta get TERM_ID META_KEY
wp term meta update TERM_ID META_KEY VAL
# User Meta
wp user meta list USER_ID
wp user meta get USER_ID META_KEY
wp user meta update USER_ID META_KEY VAL
```
---
## Posts e Conteúdo
```bash
wp post list # Listar posts
wp post list --post_type=page # Só páginas
wp post list --post_status=draft # Só rascunhos
wp post get POST_ID # Info completa
wp post create --post_type=post \
--post_title="Título" \
--post_status=publish # Criar post
wp post update POST_ID --post_title="..." # Actualizar
wp post delete POST_ID # Apagar (lixo)
wp post delete POST_ID --force # Apagar definitivo
wp post generate --count=10 # Gerar posts de teste
```
---
## Utilizadores
```bash
wp user list # Listar todos
wp user list --role=administrator # Filtrar por papel
wp user get USER_ID # Info utilizador
wp user get USERNAME --field=ID # Obter ID por username
wp user create LOGIN EMAIL \
--role=administrator \
--user_pass=SENHA # Criar
wp user update USER_ID --user_pass=SENHA # Alterar password
wp user update USER_ID --user_email=... # Alterar email
wp user set-role USER_ID administrator # Alterar papel
wp user delete USER_ID --reassign=1 # Apagar, transferindo conteúdo
wp user check-password USER_ID SENHA # Verificar password
wp user reset-password USER_ID # Reset password (envia email)
```
---
## Cache e Transientes
```bash
wp cache flush # Limpar object cache
wp cache get KEY [GROUP] # Ler cache
wp transient delete --all # Apagar todos transientes
wp transient delete NOME # Apagar transiente específico
wp transient get NOME # Ler transiente
wp rewrite flush # Regenerar rewrite rules
wp rewrite list # Listar rules actuais
```
---
## Cron
```bash
wp cron event list # Listar eventos agendados
wp cron event run HOOK_NAME # Executar agora
wp cron event delete HOOK_NAME # Apagar evento
wp cron event schedule HOOK cron now # Agendar evento
wp cron schedule list # Ver schedules disponíveis
wp cron test # Testar se cron está funcional
```
---
## wp eval — Execução PHP
```bash
# Código PHP inline
wp eval 'echo PHP_VERSION."\n";' --allow-root --path=$PATH
# Ficheiro PHP
wp eval-file script.php --allow-root --path=$PATH
# Com parâmetros
wp eval 'global $wpdb; echo $wpdb->prefix;' --allow-root --path=$PATH
# Ver constantes WordPress
wp eval 'echo WP_CONTENT_DIR."\n"; echo ABSPATH;' --allow-root --path=$PATH
# Executar com utilizador específico
wp eval 'echo get_current_user_id();' --user=1 --allow-root --path=$PATH
```
---
## Multisite
```bash
wp site list # Listar sites
wp site create --slug=subdomain # Criar site
wp site activate ID # Activar
wp site deactivate ID # Desactivar
wp site delete ID # Apagar
# Executar comando em todos os sites
wp site list --field=url | xargs -I {} wp --url={} plugin list --allow-root --path=$PATH
```
---
## WooCommerce CLI
```bash
wp wc product list # Listar produtos
wp wc product get ID # Produto específico
wp wc product create --name="Nome" \
--regular_price="19.99" # Criar produto
wp wc product update ID --regular_price="29.99" # Actualizar
wp wc product delete ID # Apagar
wp wc order list # Listar encomendas
wp wc order get ID # Encomenda específica
wp wc order update ID --status=completed # Mudar estado
wp wc customer list # Listar clientes
wp wc shipping_zone list # Zonas de entrega
```
---
## Segurança e Diagnóstico
```bash
# Verificar integridade completa
wp core verify-checksums --allow-root --path=$PATH
wp plugin verify-checksums --all --allow-root --path=$PATH
# Configuração PHP activa
wp eval 'echo PHP_VERSION."\n".php_ini_loaded_file()."\n";' --allow-root --path=$PATH
wp eval 'echo ini_get("memory_limit")."\n".ini_get("max_execution_time");' --allow-root --path=$PATH
# Ver configuração WordPress
wp option get siteurl --allow-root --path=$PATH
wp option get blogname --allow-root --path=$PATH
wp option get active_plugins --format=json --allow-root --path=$PATH
# Verificar se WP_DEBUG activo
wp eval 'echo WP_DEBUG ? "DEBUG ON" : "DEBUG OFF";' --allow-root --path=$PATH
# Listar ficheiros modificados recentemente (possível hack)
find /home/USER/public_html -name "*.php" -newer /home/USER/public_html/wp-config.php -not -path "*/wp-content/cache/*" 2>/dev/null | head -20
```
---
## Scripts de Manutenção
### Backup completo de um site
```bash
#!/bin/bash
PATH_WP="/home/USER/public_html"
BACKUP_DIR="/media/ealmeida/Dados/GDrive/Backups/WP"
DATE=$(date +%Y%m%d_%H%M)
SITE=$(basename $(dirname $PATH_WP))
mkdir -p "$BACKUP_DIR"
# Backup BD
/opt/alt/php-fpm83/usr/bin/php /usr/local/bin/wp db export \
"$BACKUP_DIR/${SITE}_db_${DATE}.sql" \
--allow-root --path=$PATH_WP
# Backup ficheiros
tar -czf "$BACKUP_DIR/${SITE}_files_${DATE}.tar.gz" \
-C $(dirname $PATH_WP) public_html \
--exclude="public_html/wp-content/cache"
echo "Backup completo: $BACKUP_DIR"
```
### Manutenção semanal (todos os sites)
```bash
#!/bin/bash
find /home -name wp-config.php -not -path "*/backup*" 2>/dev/null | while read config; do
dir=$(dirname "$config")
user=$(stat -c '%U' "$dir")
# Detectar PHP do utilizador
php_fpm=$(ps aux | grep "php-fpm: pool ${user}" | grep -v grep | head -1 | grep -o 'php-fpm[0-9][0-9]' | head -1)
php_ver=${php_fpm:-php-fpm82}
php_bin="/opt/alt/${php_ver}/usr/bin/php"
echo "=== $user ($dir) — PHP $php_ver ==="
$php_bin /usr/local/bin/wp core update --allow-root --path="$dir" 2>/dev/null
$php_bin /usr/local/bin/wp plugin update --all --allow-root --path="$dir" 2>/dev/null
$php_bin /usr/local/bin/wp theme update --all --allow-root --path="$dir" 2>/dev/null
$php_bin /usr/local/bin/wp db optimize --allow-root --path="$dir" 2>/dev/null
$php_bin /usr/local/bin/wp cache flush --allow-root --path="$dir" 2>/dev/null
done
```
---
## Flags Globais Úteis
| Flag | Descrição |
|------|-----------|
| `--allow-root` | Obrigatório no CWP (executa como root) |
| `--path=/home/USER/public_html` | Caminho da instalação WordPress |
| `--url=https://site.pt` | URL do site (importante em multisite) |
| `--user=1` | Executar como utilizador WordPress específico |
| `--format=json\|table\|csv\|ids` | Formato do output |
| `--fields=ID,name` | Campos a mostrar |
| `--debug` | Output de debug detalhado |
| `--quiet` | Sem output (para scripts) |
| `--dry-run` | Preview sem executar (onde disponível) |
| `--skip-plugins` | Não carregar plugins (troubleshooting) |
| `--skip-themes` | Não carregar temas |
---
*WP-CLI CWP — Referência Completa | Descomplicar® | v1.0.0 | 18-02-2026*

View File

@@ -0,0 +1,34 @@
#!/bin/bash
# wp-auto.sh — WP-CLI com detecção automática de PHP no CWP
# Uso: /root/wp-auto.sh --path=/home/USER/public_html [comando wp-cli]
# Exemplo: /root/wp-auto.sh --path=/home/site/public_html plugin list
# Pegar utilizador do directório de trabalho
if [[ "$*" == *"--path="* ]]; then
WP_PATH=$(echo "$@" | grep -o '\-\-path=[^ ]*' | cut -d= -f2)
else
WP_PATH=$(pwd)
fi
USER=$(stat -c '%U' "$WP_PATH" 2>/dev/null || echo "nobody")
# Encontrar versão PHP-FPM activa para este utilizador
PHP_FPM_PROCESS=$(ps aux | grep "php-fpm: pool ${USER}" | grep -v grep | head -n 1)
PHP_VERSION=$(echo "$PHP_FPM_PROCESS" | grep -o 'php-fpm[0-9][0-9]' | grep -o '[0-9][0-9]' | head -1)
if [ -z "$PHP_VERSION" ]; then
echo "⚠ Usando PHP 8.2 (padrão — pool ${USER} não encontrado)" >&2
PHP_PATH="/opt/alt/php-fpm82/usr/bin/php"
else
PHP_PATH="/opt/alt/php-fpm${PHP_VERSION}/usr/bin/php"
echo " PHP ${PHP_VERSION} detectado para utilizador ${USER}" >&2
fi
# Verificar se o PHP existe
if [ ! -f "$PHP_PATH" ]; then
echo "❌ PHP não encontrado: $PHP_PATH — usando PHP 8.3" >&2
PHP_PATH="/opt/alt/php-fpm83/usr/bin/php"
fi
# Executar WP-CLI
exec "$PHP_PATH" /usr/local/bin/wp --allow-root "$@"

View File

@@ -303,7 +303,7 @@ Em caso de dúvidas ou para aprofundar conhecimento, consultar os seguintes data
```javascript
// Desenvolvimento de plugins WordPress
mcp__dify-kb__dify_kb_retrieve_segments({
mcp__notebooklm__notebook_query, mcp__dify-kb__dify_kb_retrieve_segments({
dataset_id: "9da0b2b9-5051-4b99-b9f6-20bf35067092",
query: "plugin development hooks filters"
})

View File

@@ -0,0 +1,266 @@
---
name: wp-translate
description: >
Tradução WordPress PT-PT com DeepL Pro, glossário e scripts próprios. Use when translating WordPress plugins to PT-PT, fixing PT-BR translations, handling .po/.mo/.json files, working with Loco Translate, deploying translations to starter.descomplicar.pt, or when user mentions "tradução", "translate", "pt-pt", "pt-br", ".po", ".mo", "loco", "deepl wordpress".
author: Descomplicar® Crescimento Digital
version: 1.0.0
quality_score: 100
user_invocable: true
category: wordpress
tags: [wordpress, traducao, pt-pt, deepl, po, mo, gettext, loco-translate, i18n]
desk_task: 1580
desk_project: 65
allowed-tools: Read, Write, Edit, Bash, mcp__ssh-unified, mcp__deepl, mcp__memory-supabase
mcps: ssh-unified, deepl, memory-supabase
---
# /wp-translate — Traduções WordPress PT-PT
## Visão Geral
Skill para traduzir plugins WordPress para Português Europeu (PT-PT) usando DeepL Pro com glossário, scripts de automação próprios e deploy directo para starter.descomplicar.pt.
---
## Scripts Disponíveis
**Localização local:** `/media/ealmeida/Dados/Dev/Scripts/translate-wp-plugin/`
**No servidor (tmp):** `/tmp/translate_missing.py`, `/tmp/fix_ptbr_safe.py`
| Script | Função |
|--------|--------|
| `translate_missing.py` | Traduz strings vazias via DeepL Pro + glossário. Suporta batch 50 strings/pedido |
| `fix_ptbr_safe.py` | Corrige ~100 padrões PT-BR → PT-PT |
| `fix_malformed.py` | Corrige sintaxe .po mal formada |
| `batch_process_library.py` | Orquestra fix_malformed → fix_ptbr → translate → compile .mo para toda a biblioteca |
| `setup_glossary.py` | Cria/actualiza glossário DeepL (175 entradas WP PT-PT) |
**Configuração (.env):**
```
DEEPL_API_KEY=3c74e27a-f688-4de2-927b-e91da46cf9cd
DEEPL_GLOSSARY_ID=b9db5639-c201-479f-87af-adb81f112067
```
---
## DeepL Pro
- **Endpoint:** `api.deepl.com` (Pro — sem `:fx`)
- **Quota:** ~1,6M / 1 trilião de chars
- **Glossário:** 160 entradas brand names + termos WordPress (EN→PT)
- **Atenção:** DeepL também gera padrões PT-BR — sempre correr `fix_ptbr` ANTES E DEPOIS da tradução
---
## Workflow: Plugin Individual
```bash
# 1. No servidor — gerar .pot (se não existir)
wp --path=/home/ealmeida/starter.descomplicar.pt i18n make-pot \
wp-content/plugins/PLUGIN/ \
/tmp/PLUGIN-pt_PT.po \
--domain=TEXT_DOMAIN --allow-root
# 2. Traduzir com DeepL Pro
python3 /tmp/translate_missing.py /tmp/PLUGIN-pt_PT.po
# 3. Corrigir PT-BR pós-tradução
python3 /tmp/fix_ptbr_safe.py /tmp/PLUGIN-pt_PT.po
# 4. Para plugins React — gerar JSON para strings JS
wp --path=/home/ealmeida/starter.descomplicar.pt i18n make-json \
/tmp/PLUGIN-pt_PT.po /path/to/languages/ --no-purge --allow-root
# 5. Copiar para servidor
cp /tmp/PLUGIN-pt_PT.{po,mo} /home/ealmeida/starter.descomplicar.pt/wp-content/languages/plugins/
chown ealmeida:ealmeida /home/ealmeida/starter.descomplicar.pt/wp-content/languages/plugins/PLUGIN-pt_PT.*
```
## Workflow: Biblioteca Completa
```bash
python3 /media/ealmeida/Dados/Dev/Scripts/translate-wp-plugin/batch_process_library.py \
/path/to/library
# Opções: --skip-translate, --dry-run, --only-ptbr
```
---
## Encontrar o Text Domain
**Crítico:** O nome da pasta do plugin ≠ text domain. Sempre verificar:
```bash
grep "Text Domain" /path/to/plugin/plugin-main.php
```
**Casos conhecidos de divergência:**
| Pasta | Text Domain | Ficheiro .po |
|-------|-------------|--------------|
| `pro-elements` | `elementor-pro` | `elementor-pro-pt_PT.po` |
| `branda-white-labeling` | `ub` | `ub-pt_PT.po` |
| `seo-by-rank-math` | `rank-math` | `rank-math-pt_PT.po` |
---
## Plugins React/JS — Ficheiros JSON
Plugins com `wp_set_script_translations()` precisam de um `.json` além do `.mo`.
**Detectar:**
```bash
grep -r "wp_set_script_translations" /path/plugin/ --include="*.php"
```
**Gerar JSON:**
```bash
wp i18n make-json PLUGIN-pt_PT.po /path/languages/ --no-purge --allow-root
```
**Atenção ao MD5:** WordPress calcula `md5(relative_path_of_script)` para o nome do JSON.
Se o `source` no JSON não bater com o script registado, copiar o JSON com o MD5 correcto:
```bash
# Encontrar qual script está registado
grep "wp_register_script\|wp_enqueue_script" plugin.php | grep "HANDLE"
# Calcular MD5 do caminho relativo
python3 -c "import hashlib; print(hashlib.md5('app/index.js'.encode()).hexdigest())"
# Copiar com nome correcto
cp PLUGIN-pt_PT-{md5_errado}.json PLUGIN-pt_PT-{md5_correcto}.json
```
**Exemplo real — ai-engine:**
- Handle `mwai` → script `app/index.js` → MD5: `2018087f584c4398b5c3a23fc0e5f9db`
- JSON gerado com source `app/i18n.js` → MD5: `0bdc92e05d8f3ab638aa855679db059e`
- Solução: copiar JSON com o MD5 de `app/index.js`
---
## Padrões PT-BR Comuns (fix_ptbr)
DeepL introduz PT-BR mesmo com `target_lang=PT-PT`. Correr fix_ptbr **depois** da tradução:
| PT-BR | PT-PT |
|-------|-------|
| atualiz* | actualiz* |
| ativar/desativar | activar/desactivar |
| você/vocês | o utilizador/os utilizadores |
| excluir/excluído | eliminar/eliminado |
| acessar | aceder |
| compartilhar | partilhar |
| postagem/postagens | publicação/publicações |
| ativo/inativo | activo/inactivo |
| ativação/desativação | activação/desactivação |
| digite | introduza |
| clique | clique (OK) |
---
## Servidor starter.descomplicar.pt
```
SSH: mcp__ssh-unified__ssh_execute server:"server"
Path traduções: /home/ealmeida/starter.descomplicar.pt/wp-content/languages/plugins/
Loco Translate: /home/ealmeida/starter.descomplicar.pt/wp-content/languages/loco/plugins/
WP-CLI: wp --path=/home/ealmeida/starter.descomplicar.pt [...] --allow-root
```
**Permissões obrigatórias após operações root:**
```bash
chown ealmeida:ealmeida /home/ealmeida/starter.descomplicar.pt/wp-content/languages/plugins/PLUGIN-pt_PT.*
```
**Deploy completo:**
```bash
# Copiar .po + .mo + .json
cp /tmp/PLUGIN-pt_PT.{po,mo} /home/.../languages/plugins/
# Se React: copiar também os .json
chown ealmeida:ealmeida /home/.../languages/plugins/PLUGIN-pt_PT*
wp --path=... cache flush --allow-root
```
---
## Verificação de Estado
```bash
# Strings em falta em todos os plugins
python3 -c "
from pathlib import Path; import polib
for f in sorted(Path('languages/plugins').glob('*-pt_PT.po')):
po = polib.pofile(str(f))
empty = len(po.untranslated_entries())
if empty: print(f'{f.name}: {empty} em falta')
"
# PT-BR residual após tradução
grep -rn "atualiz\|ativar\|você\b\|excluir\b" languages/plugins/*.po
```
---
## Problemas Conhecidos
### Python 3.6 no servidor
Scripts locais usam type hints Python 3.9+. Para correr no servidor:
- Remover anotações `-> type` das funções
- Não usar `from __future__ import annotations` (não funciona no 3.6)
- `tuple[str, list]` → usar `Tuple` de typing
### msgfmt vs polib
`msgfmt 0.19.8.1` recusa .po com strings mal formadas. Usar sempre **polib** para compilar:
```python
po = polib.pofile('file.po')
po.save_as_mofile('file.mo')
```
### Glossário Pro vs Free
Glossário criado com chave `:fx` (Free) não funciona na conta Pro e vice-versa.
Fix: `python3 setup_glossary.py` após mudar de chave API.
### XML tag_handling quebra com &
`tag_handling='xml'` falha com strings como "Settings & Tools".
Fix actual: tokens Unicode `⟦0⟧` como placeholders (implementado em translate_missing.py v2.1+).
### Strings hardcoded em bundles JS compilados
Plugins com React que não usam `__()` nas strings de UI (ex: `subtitle:"text"`) não podem ser traduzidos.
Não é possível sem modificar o bundle compilado. Aceitar e documentar.
### Loco Translate "Write protected"
Ficheiros criados por root via SSH ficam inacessíveis ao web server.
Fix: `chown ealmeida:ealmeida` nos ficheiros de tradução.
---
## Compilar .mo após Edições Manuais
```python
import polib
po = polib.pofile('/path/to/file.po')
po.save('/path/to/file.po') # salva o .po actualizado
po.save_as_mofile('/path/to/file.mo')
```
## Re-gerar JSON após Edições ao .po
```bash
wp i18n make-json file-pt_PT.po /path/languages/ --no-purge --allow-root
# Depois sempre copiar com MD5 correcto se necessário
chown ealmeida:ealmeida /path/languages/file-pt_PT-*.json
wp --path=... cache flush --allow-root
```
---
## Estado da Biblioteca (starter.descomplicar.pt — 25-02-2026)
- **21 plugins** com ficheiros .po activos
- **Cobertura:** 100% em todos os plugins (excl. webp-express sem suporte i18n)
- **Strings hardcoded** não traduzíveis: ~7 subtítulos no ai-engine (limitação do plugin)
- **Loco Translate path:** `languages/loco/plugins/` para pro-elements (elementor-pro)
---
*Skill v1.0.0 | 25-02-2026 | Descomplicar®*

View File

@@ -4,7 +4,7 @@ description: WordPress core, plugins, and themes update management. Safely updat
WordPress components with backups. Use when user mentions "wordpress update", "wp
update", "update plugins", "update themes", "wordpress maintenance".
author: Descomplicar® Crescimento Digital
version: 1.1.0
version: 1.2.0
quality_score: 75
user_invocable: true
desk_task: 1553
@@ -91,6 +91,63 @@ chown -R USER:USER /home/USER/PATH/wp-content/
Re-executar script de verificação para confirmar que todos os sites continuam funcionais.
### 4. Relatório Desk CRM (OBRIGATÓRIO)
Após concluir as actualizações e verificação final, publicar relatório completo como comentário na **Discussão #52** ("Logs de Atualização de Websites") do **Projecto #69**.
**Tool:** `mcp__desk-crm-v3__add_discussion_comment`
- `discussion_id`: 52
- `staff_id`: 25 (AikTop)
- `content`: HTML formatado (template abaixo)
**Template HTML do Relatório:**
```html
<h4>🔄 Relatório WordPress Update - YYYY-MM-DD</h4>
<h5>Verificação Inicial</h5>
<ul>
<li>✅ X/16 sites OK</li>
<li>❌ Y sites com problemas: [lista se houver]</li>
</ul>
<h5>Actualizações Aplicadas</h5>
<table border="1" cellpadding="5" cellspacing="0" style="border-collapse:collapse;width:100%">
<tr style="background:#f0f0f0"><th>Site</th><th>Plugins</th><th>Temas</th><th>Core</th><th>Notas</th></tr>
<tr><td>site.pt</td><td>✅ 3/3</td><td>✅ 1/1</td><td></td><td></td></tr>
<!-- uma linha por site com alterações -->
</table>
<p><strong>Sites sem alterações:</strong> [lista de sites já actualizados]</p>
<h5>Falhas (Licenças Premium)</h5>
<table border="1" cellpadding="5" cellspacing="0" style="border-collapse:collapse;width:100%">
<tr style="background:#fff3cd"><th>Plugin</th><th>Sites Afectados</th><th>Erro</th></tr>
<tr><td>plugin-name</td><td>site1, site2</td><td>Unauthorized</td></tr>
</table>
<h5>Verificação Final</h5>
<ul>
<li>✅ X/16 sites OK após actualizações</li>
</ul>
<h5>Alertas Wordfence</h5>
<ul>
<li>[Listar alertas Wordfence recentes da conta it@descomplicar.pt se existirem]</li>
<li>Sem alertas (se nenhum)</li>
</ul>
<hr>
<p><em>Relatório automático gerado por /wp-update v1.2.0 | AikTop</em></p>
```
**Regras do Relatório:**
- Verificar data actual com `mcp__mcp-time__current_time` antes de gerar
- Incluir TODOS os sites (16), mesmo os que não tinham updates
- Separar claramente sucessos de falhas
- Listar alertas Wordfence da pasta `INBOX.Wordpress.Alertas Wordfence` da conta `it` (últimos 7 dias)
- Se a verificação final detectar problemas, destacar em **vermelho**
- Omitir a tabela de Falhas se não houver nenhuma
---
## PATHS DOS SITES
@@ -161,11 +218,27 @@ Workflow segunda-feira:
2. Executa verificação inicial
3. Reporta estado dos sites
4. Propõe actualizações pendentes
5. Após conclusão, publica relatório na Discussão #52 (Projecto #69)
## INTEGRAÇÃO DESK CRM
- **Projecto:** #69 (Manutenção Websites)
- **Discussão:** #52 (Logs de Atualização de Websites)
- **Staff:** 25 (AikTop) para comentários automáticos
- **Tool:** `mcp__desk-crm-v3__add_discussion_comment`
- **Wordfence Alerts:** Conta IMAP `it`, pasta `INBOX.Wordpress.Alertas Wordfence`
---
## CHANGELOG
### v1.2.0 (2026-02-08)
- Adicionado Passo 4: Relatório automático Desk CRM
- Relatório publicado como comentário na Discussão #52 (Projecto #69)
- Template HTML completo com tabelas de actualizações e falhas
- Integração Wordfence: verifica alertas recentes da conta IMAP it@
- Staff AikTop (ID 25) como autor dos relatórios
### v1.1.0 (2026-02-02)
- Script movido para `/media/ealmeida/Dados/Dev/ClaudeDev/Claude-Scripts/wp-update/`
- Removidos domínios expirados da lista (jornadadoheroi, tecoworking, socialboost)