docs: doc 12 blueprint Widget Builder (spec->widget Elementor PHP)
Fundamentado na API publica real do Elementor (Widget_Base, Controls_Manager, Widgets_Manager, Elements_Manager, widget core Heading como exemplo) mais os padroes de seguranca ja confirmados em codigo real Free (EMCP_Tools_Widget_Store/ Widget_Loader do doc06 - manifest-only, tamper guard sha256, path containment, runtime_validate). O compilador spec->PHP em si (EMCP_Tools_Widget_Generator) e a camada de abilities MCP continuam ausentes do build Free - marcado [REAL] vs [DESENHO PROPRIO] em cada afirmacao do documento. Recomenda 'zero tool de activacao via MCP' para set-widget-status, seguindo a mesma filosofia ja confirmada no Themer PHP e nos PHP Snippets (aprovacao humana obrigatoria antes de codigo gerado por IA correr em producao). Actualiza INDEX.md com a 13a entrada e totais revistos.
This commit is contained in:
@@ -0,0 +1,577 @@
|
|||||||
|
# 12 — Widget Builder: blueprint de abilities MCP (desenho próprio, não é auditoria EMCP Pro)
|
||||||
|
|
||||||
|
Fonte primária: leitura directa (19-08-2026) do **Elementor 4.2.2** (build Free) instalado em
|
||||||
|
`emanuelalmeida.pt` (`/home/ealmeida/emanuelalmeida.pt/wp-content/plugins/elementor/`) — a API
|
||||||
|
pública real com que qualquer gerador de widgets tem de trabalhar. Cruzado com
|
||||||
|
`docs/06-SANDBOX-CUSTOM-CODE.md` §3.9-3.10 (código real, Free, já lido nesta série:
|
||||||
|
`EMCP_Tools_Widget_Store` e `EMCP_Tools_Widget_Loader`) e `docs/04-THEMER.md` §11 (o padrão de
|
||||||
|
aprovação humana do Themer PHP). **Este documento NÃO lê nem audita o compilador spec→PHP nem a
|
||||||
|
camada de abilities MCP do Widget Builder Pro** — ambos estão fisicamente ausentes da árvore Free
|
||||||
|
instalada (confirmado em `docs/06-SANDBOX-CUSTOM-CODE.md` §3.9, citação directa: *"Também
|
||||||
|
confirmado ausente: `includes/class-widget-generator.php` (o compilador spec→PHP) e
|
||||||
|
`includes/class-block-store.php`"*; e `includes/abilities/class-widget-builder-abilities.php`
|
||||||
|
também ausente da listagem de `includes/abilities/`).
|
||||||
|
|
||||||
|
## 0. Panorama — porque este documento é diferente dos outros 11 e o que fundamenta cada peça
|
||||||
|
|
||||||
|
A captura de ecrã da UI de administração do EMCP Tools (secção *Widget Builder*, marcada
|
||||||
|
"Requires EMCP Pro") mostra **8 tools MCP**, todas prefixadas `emcp-tools/`:
|
||||||
|
`list-control-types`, `validate-widget-spec`, `create-custom-widget`, `update-custom-widget`,
|
||||||
|
`get-custom-widget`, `list-custom-widgets`, `set-widget-status`, `delete-custom-widget`. Estes 8
|
||||||
|
nomes são usados **apenas como especificação da API-alvo** (nomenclatura/âmbito de cada tool) —
|
||||||
|
**não** como fonte de implementação. Não há código Pro nesta árvore para ler, logo não há
|
||||||
|
descrições de input schema, mensagens de erro, nem lógica interna do EMCP Pro para citar.
|
||||||
|
|
||||||
|
Em vez disso, este documento responde à pergunta inversa dos docs 01-10: **dado o que o
|
||||||
|
Elementor em si expõe como API pública de widgets** (`\Elementor\Widget_Base`,
|
||||||
|
`Controls_Manager`, `Widgets_Manager`, `Elements_Manager`) **e dado o modelo de segurança de
|
||||||
|
código-gerado-por-IA já confirmado e testado noutras partes desta mesma árvore Free** (Sandbox de
|
||||||
|
PHP Snippets, Themer PHP, e a camada de armazenamento `EMCP_Tools_Widget_Store` que **existe**
|
||||||
|
mesmo sem a camada de abilities), **como desenharíamos do zero um sistema "Widget Builder"**?
|
||||||
|
|
||||||
|
Três fontes reais fundamentam cada peça deste blueprint, e cada secção abaixo identifica
|
||||||
|
explicitamente qual delas usa:
|
||||||
|
|
||||||
|
1. **API pública real do Elementor Free** (lida nesta sessão): `includes/base/widget-base.php`
|
||||||
|
(`Widget_Base`, classe abstracta que qualquer widget custom tem de estender),
|
||||||
|
`includes/managers/controls.php` (`Controls_Manager`, catálogo real de tipos de controlo),
|
||||||
|
`includes/managers/widgets.php` (`Widgets_Manager`, registo/lookup de widgets),
|
||||||
|
`includes/managers/elements.php` (`Elements_Manager`, categorias), e
|
||||||
|
`includes/widgets/heading.php` (`Widget_Heading`, um widget core real como exemplo concreto de
|
||||||
|
`register_controls()`/`render()`).
|
||||||
|
2. **Padrões de segurança já confirmados (código real, Free) em `docs/06-SANDBOX-CUSTOM-CODE.md`**
|
||||||
|
— nomeadamente `EMCP_Tools_Widget_Store` (§3.9, **existe** na build Free, 839 linhas lidas na
|
||||||
|
íntegra) e `EMCP_Tools_Widget_Loader` (§3.10, idem), mais o validador de PHP partilhado
|
||||||
|
(`EMCP_Tools_PHP_Snippet_Validator`, §3.3) e o padrão de aprovação humana do Themer PHP
|
||||||
|
(`docs/04-THEMER.md` §11-§2, também código real).
|
||||||
|
3. **O catálogo de 8 nomes de tool da captura de ecrã** — usado só como lista de âmbito/naming
|
||||||
|
alvo (secção 7).
|
||||||
|
|
||||||
|
> **Convenção usada em todo o documento**: cada afirmação é marcada `[REAL]` quando vem
|
||||||
|
> directamente do código lido (Elementor ou EMCP Free já documentado nesta série), ou
|
||||||
|
> `[DESENHO PRÓPRIO]` quando é uma proposta nossa sem equivalente confirmado no EMCP Pro. Onde uma
|
||||||
|
> peça combina as duas (ex.: reaproveitar uma classe real do EMCP Free para uma função nova), isso
|
||||||
|
> é dito explicitamente.
|
||||||
|
|
||||||
|
**A distinção mais importante de todo o documento:** a camada de **armazenamento** de um widget
|
||||||
|
custom (`EMCP_Tools_Widget_Store`/`EMCP_Tools_Widget_Loader`) **já existe e é real** — só a camada
|
||||||
|
que a alimenta (o compilador spec→PHP, `EMCP_Tools_Widget_Generator`) e a camada que a expõe a
|
||||||
|
agentes de IA (`EMCP_Tools_Widget_Builder_Abilities`) é que estão ausentes e são, portanto, o
|
||||||
|
verdadeiro objecto deste desenho próprio. As secções 6 e parte da 7 apoiam-se fortemente em código
|
||||||
|
real já lido; as secções 1, 4 e 5 são as mais "desenho próprio" deste documento.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. A "spec" de um widget — estrutura de dados proposta `[DESENHO PRÓPRIO]`
|
||||||
|
|
||||||
|
Um agente de IA nunca deveria escrever PHP cru para criar um widget — a citação já confirmada em
|
||||||
|
`docs/06-SANDBOX-CUSTOM-CODE.md` §3.9 sobre o `EMCP_Tools_Widget_Store` real é explícita quanto a
|
||||||
|
isto: *"estes widgets são PHP compilado por este plugin a partir de uma spec fornecida por IA (a
|
||||||
|
IA nunca escreve PHP cru)"*. Adoptamos o mesmo princípio de design aqui — a spec é a única
|
||||||
|
superfície que um agente manipula; um compilador (secção 5) gera o PHP.
|
||||||
|
|
||||||
|
Proposta de schema JSON (nomes de campo nossos, `[DESENHO PRÓPRIO]`; os TIPOS de controlo válidos
|
||||||
|
dentro de `controls[].type` são fundamentados em `Controls_Manager::get_controls_names()`, `[REAL]`
|
||||||
|
— ver secção 3):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "testimonial_card",
|
||||||
|
"title": "Testimonial Card",
|
||||||
|
"icon": "eicon-testimonial",
|
||||||
|
"category": "general",
|
||||||
|
"keywords": ["testimonial", "quote", "review"],
|
||||||
|
"controls": [
|
||||||
|
{
|
||||||
|
"id": "quote_text",
|
||||||
|
"type": "textarea",
|
||||||
|
"label": "Quote",
|
||||||
|
"default": "Add your quote here",
|
||||||
|
"section": "content",
|
||||||
|
"tab": "content"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "author_name",
|
||||||
|
"type": "text",
|
||||||
|
"label": "Author",
|
||||||
|
"default": "",
|
||||||
|
"section": "content",
|
||||||
|
"tab": "content"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "author_photo",
|
||||||
|
"type": "media",
|
||||||
|
"label": "Photo",
|
||||||
|
"default": { "url": "" },
|
||||||
|
"section": "content",
|
||||||
|
"tab": "content"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "card_bg_color",
|
||||||
|
"type": "color",
|
||||||
|
"label": "Background",
|
||||||
|
"default": "#ffffff",
|
||||||
|
"section": "style",
|
||||||
|
"tab": "style",
|
||||||
|
"selectors": { "{{WRAPPER}} .emcpb-card": "background-color: {{VALUE}};" }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sections": [
|
||||||
|
{ "id": "content", "label": "Content", "tab": "content" },
|
||||||
|
{ "id": "style", "label": "Card Style", "tab": "style" }
|
||||||
|
],
|
||||||
|
"render_template": "<div class=\"emcpb-card\" style=\"background:{{card_bg_color}}\"><blockquote>{{quote_text}}</blockquote><cite>{{author_name}}</cite></div>",
|
||||||
|
"css": ".emcpb-card{padding:24px;border-radius:8px}",
|
||||||
|
"js": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Campos e a sua justificação:
|
||||||
|
|
||||||
|
| Campo | Fundamentação | Nota |
|
||||||
|
|---|---|---|
|
||||||
|
| `name` | `[REAL]` — mapeado directamente para `Widget_Base::get_name()`, o identificador único que `Widgets_Manager::register()` usa como chave (`$this->_widget_types[$widget_instance->get_name()] = $widget_instance`, `includes/managers/widgets.php:283`) | Tem de ser único no site (secção 4) |
|
||||||
|
| `title`/`icon`/`category`/`keywords` | `[REAL]` — mapeiam 1:1 para `get_title()`, `get_icon()` (default `'eicon-apps'`), `get_categories()` (default `['general']`), `get_keywords()` (default `[]`), todos métodos *overridable* confirmados em `widget-base.php` | `category` é singular na nossa spec por simplicidade; Elementor aceita array (`get_categories()` devolve `array`) — o compilador (secção 5) gera sempre `return [ $spec['category'] ];` |
|
||||||
|
| `controls[]` | `[DESENHO PRÓPRIO]` (a lista), `type` de cada item `[REAL]` (secção 3) | Cada item vira uma chamada `add_control()` |
|
||||||
|
| `controls[].section`/`sections[]` | `[DESENHO PRÓPRIO]`, mapeado para `start_controls_section()`/`end_controls_section()` (`[REAL]`, método herdado, usado exactamente assim em `heading.php`: `$this->start_controls_section('section_title', ['label' => ...])`) | Agrupa controlos em secções colapsáveis no editor |
|
||||||
|
| `controls[].tab` | `[REAL]` os valores válidos (`Controls_Manager::TAB_CONTENT`/`TAB_STYLE`/`TAB_ADVANCED`/etc., constantes confirmadas em `controls.php`) | Default `content` se omitido |
|
||||||
|
| `controls[].selectors` | `[REAL]` — mecanismo Elementor nativo de CSS live-preview, visto usado extensivamente em `heading.php` (ex. `'{{WRAPPER}} .elementor-heading-title' => 'color: {{VALUE}};'`) | Só faz sentido nos controlos de Style |
|
||||||
|
| `render_template` | `[DESENHO PRÓPRIO]` — string com placeholders `{{control_id}}`, ver secção 5 | Alternativa a fornecer PHP raw (que passaria pelo validador do sandbox, secção 6) |
|
||||||
|
| `css`/`js` | `[DESENHO PRÓPRIO]`, mas o mecanismo de assets opcionais por widget (ficheiro `.css`/`.js` ao lado do `.php` gerado) é `[REAL]` — confirmado em `EMCP_Tools_Widget_Store` (`docs/06` §3.9: `wp-content/emcp-sandbox/widgets/{id}/widget.php` `+ style.css/script.js` opcionais) | Reaproveitado tal-qual, ver secção 6 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Como um widget é normalmente registado — contrato real do Elementor `[REAL]`
|
||||||
|
|
||||||
|
Fundação obrigatória para qualquer gerador: um widget Elementor é uma instância de uma classe PHP
|
||||||
|
que estende `\Elementor\Widget_Base` (`includes/base/widget-base.php:29`, `abstract class
|
||||||
|
Widget_Base extends Element_Base`), registada via `Widgets_Manager::register(Widget_Base
|
||||||
|
$widget_instance)` (`includes/managers/widgets.php:261`), chamada dentro do hook
|
||||||
|
`elementor/widgets/register` (confirmado directamente: `do_action('elementor/widgets/register',
|
||||||
|
$this)`, `includes/managers/widgets.php:159`, substituiu o antigo
|
||||||
|
`elementor/widgets/widgets_registered` deprecated desde a 3.5.0). Este é exactamente o mesmo hook
|
||||||
|
que `docs/04-THEMER.md` §10 já confirmou ser usado pelo `EMCP_Tools_Themer_Widgets` (widgets
|
||||||
|
dinâmicos do Themer) — `require_once` tardio da classe custom dentro deste hook, "quando
|
||||||
|
`\Elementor\Widget_Base` está garantidamente carregado".
|
||||||
|
|
||||||
|
Contrato mínimo de métodos que uma classe de widget tem de implementar/pode sobrepor (confirmados
|
||||||
|
por leitura directa de `widget-base.php` + o exemplo real `Widget_Heading`):
|
||||||
|
|
||||||
|
| Método | Obrigatório? | Confirmado em |
|
||||||
|
|---|---|---|
|
||||||
|
| `get_name(): string` | Sim (abstracto, herdado de `Element_Base`/`Controls_Stack` — não visível no corpo de `Widget_Base`, mas usado como chave única em `register()`) | `Widgets_Manager::register()`, `Widget_Heading::get_name()` (`return 'heading';`) |
|
||||||
|
| `get_title(): string` | Sim (idem) | `Widget_Heading::get_title()` |
|
||||||
|
| `get_icon(): string` | Não — default `'eicon-apps'` | `widget-base.php` (método com corpo default) |
|
||||||
|
| `get_categories(): array` | Não — default `['general']` | `widget-base.php` |
|
||||||
|
| `get_keywords(): array` | Não — default `[]` | `widget-base.php` |
|
||||||
|
| `register_controls(): void` | Sim, `protected` | Chamado internamente por `Controls_Stack::init_controls()`; ver `Widget_Heading::register_controls()` completo, usa `start_controls_section()`/`add_control()`/`add_responsive_control()`/`add_group_control()`/`end_controls_section()` |
|
||||||
|
| `render(): void` | Sim, `protected` | `Widget_Heading::render()`: lê `$this->get_settings_for_display()`, monta HTML, faz `echo` (com comentário PHPCS explícito de que a variável já está segura — `wp_kses_post()` aplicado antes) |
|
||||||
|
| `content_template(): void` | Não | Template Backbone.js para preview live no editor sem round-trip ao servidor — **fora de âmbito de um gerador v1** (ver secção 8) |
|
||||||
|
|
||||||
|
`get_stack($with_common_controls = true)` (`widget-base.php`, confirmado) é o método que força a
|
||||||
|
resolução completa do stack de controlos de um widget — internamente chama `init_controls()`
|
||||||
|
(que, em `Widget_Base`, reinicia a flag `is_first_section` e depois chama
|
||||||
|
`parent::init_controls()`, o qual invoca `register_controls()`). **Isto é directamente relevante
|
||||||
|
para a secção 6**: instanciar uma classe gerada e chamar `get_stack()` é a forma real de forçar
|
||||||
|
`register_controls()` a correr e apanhar um erro de sintaxe/lógica ANTES do widget chegar ao
|
||||||
|
editor — e é exactamente o que `EMCP_Tools_Widget_Store::runtime_validate()` já faz, confirmado em
|
||||||
|
`docs/06` §3.9.
|
||||||
|
|
||||||
|
**Categorias** (`Elements_Manager::get_categories()`, `includes/managers/elements.php:121`,
|
||||||
|
`init_categories()` lido na íntegra) — as nativas confirmadas nesta versão: `v4-elements`
|
||||||
|
("Atomic Elements"), `layout`, `basic`, `pro-elements`, `helloplus`, `general`, `link-in-bio`,
|
||||||
|
`theme-elements`, `woocommerce-elements` (+ `atomic-form` condicional ao experiment
|
||||||
|
`e_atomic_elements`). Categorias custom registam-se via `Elements_Manager::add_category($name,
|
||||||
|
$properties)` no hook `elementor/elements/categories_registered` — o mesmo hook que
|
||||||
|
`docs/04-THEMER.md` §10 confirma ser usado pelo Themer para a sua categoria "EMCP Themer".
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. `list-control-types` — catálogo real de tipos de controlo `[REAL]` + parâmetros `[misto]`
|
||||||
|
|
||||||
|
A lista de tipos vem directamente das constantes de `Controls_Manager`
|
||||||
|
(`includes/managers/controls.php`), cada uma com o seu próprio PHPDoc de uma linha no código-fonte
|
||||||
|
(citado tal-qual abaixo) e devolvida por `Controls_Manager::get_controls_names()` (método real,
|
||||||
|
usado internamente por `register_controls()` da própria classe para instanciar `Control_{Nome}`
|
||||||
|
por convenção de nome de classe — `str_replace('_',' ',ucwords(...))`, confirmado). A ability
|
||||||
|
`list-control-types` proposta devolveria exactamente este catálogo.
|
||||||
|
|
||||||
|
### 3.1 Controlos de dados simples/compostos (`get_controls_names()`)
|
||||||
|
|
||||||
|
| Constante | Valor | Descrição (PHPDoc real) | Parâmetros confirmados em uso real (`heading.php`) |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `TEXT` | `text` | "Text control." | Não exemplificado directamente em `heading.php` (usa `TEXTAREA`); campo de linha única, aceita `label`/`default`/`placeholder`/`dynamic` por convenção Elementor |
|
||||||
|
| `NUMBER` | `number` | "Number control." | Não exemplificado |
|
||||||
|
| `TEXTAREA` | `textarea` | "Textarea control." | `heading.php`: `label`, `type`, `ai:{type:'text'}` (marcador de compatibilidade com AI writer nativo do Elementor), `dynamic:{active:true}`, `placeholder`, `default` |
|
||||||
|
| `SELECT` | `select` | "Select control." | `heading.php`: `label`, `options` (mapa `valor => label`), `default`, `condition` (ex. `'size!' => 'default'`, esconde o controlo condicionalmente a outro valor) |
|
||||||
|
| `SWITCHER` | `switcher` | "Switcher control." | Não exemplificado; toggle on/off, aceita `label_on`/`label_off`/`return_value` por convenção |
|
||||||
|
| `BUTTON` | `button` | "Button control." | Não exemplificado |
|
||||||
|
| `HIDDEN` | `hidden` | "Hidden control." | Usado internamente por `Widget_Base::register_skin_control()` (`'_skin'` quando só há 1 skin) |
|
||||||
|
| `HEADING` | `heading` | "Heading control." | Separador de UI, sem valor associado |
|
||||||
|
| `RAW_HTML` | `raw_html` | "Raw HTML control." | Usado extensivamente no próprio `Controls_Manager` para avisos de upsell Pro; aceita `raw` (string HTML) |
|
||||||
|
| `NOTICE`/`DEPRECATED_NOTICE`/`ALERT` | `notice`/`deprecated_notice`/`alert` | Controlos de aviso/UI informativa | `Widget_Base::add_deprecation_message()` usa `ALERT` com `alert_type`/`content` |
|
||||||
|
| `POPOVER_TOGGLE`/`SECTION`/`TAB`/`TABS`/`DIVIDER` | idem | Controlos estruturais de UI | `DIVIDER` usado em `heading.php`: `['type' => Controls_Manager::DIVIDER]`, sem outros parâmetros |
|
||||||
|
| `COLOR` | `color` | "Color control." | `heading.php`: `label`, `global:{default: Global_Colors::COLOR_PRIMARY}` (liga a paleta global do kit), `selectors` |
|
||||||
|
| `MEDIA` | `media` | "Media control." | Não exemplificado em `heading.php`; aceita objecto `{url, id}`, tipicamente com `media_types:['image']` |
|
||||||
|
| `SLIDER` | `slider` | "Slider control." | `heading.php` (`title_hover_color_transition_duration`): `size_units:['s','ms','custom']`, `default:{unit:'s'}`, `selectors` com `{{SIZE}}{{UNIT}}` |
|
||||||
|
| `DIMENSIONS`/`IMAGE_DIMENSIONS` | `dimensions`/`image_dimensions` | Controlos de 4 valores (top/right/bottom/left) + unidade | Não exemplificado |
|
||||||
|
| `CHOOSE` | `choose` | "Choose control." | `heading.php` (`align`): `options` (mapa `valor => {title, icon}`), `selectors_dictionary` (remapeamento de valor consoante RTL/LTR), `toggle` implícito |
|
||||||
|
| `VISUAL_CHOICE` | `visual_choice` | "Visual_Choice control." | Não exemplificado |
|
||||||
|
| `WYSIWYG` | `wysiwyg` | "WYSIWYG control." | Editor rich-text (TinyMCE); não exemplificado |
|
||||||
|
| `CODE` | `code` | "Code control." | Editor CodeMirror com `language` param; não exemplificado |
|
||||||
|
| `FONT` | `font` | "Font control." | Não exemplificado |
|
||||||
|
| `GAPS` | `gaps` | "Gaps control." | Não exemplificado (par row/column) |
|
||||||
|
| `WP_WIDGET` | `wp_widget` | "WordPress widget control." | Embrulha um widget clássico WordPress dentro do Elementor |
|
||||||
|
| `URL` | `url` | "URL control." | `heading.php`: `label`, `dynamic:{active:true}`, `default:{url:''}` |
|
||||||
|
| `REPEATER` | `repeater` | "Repeater control." | Não exemplificado em `heading.php`; array de sub-campos repetíveis (padrão para listas — ex. itens de accordion, slides) |
|
||||||
|
| `ICON`/`ICONS` | `icon`/`icons` | Selecção de ícone (biblioteca antiga vs nova unificada, `ICONS` é o formato actual) | Não exemplificado |
|
||||||
|
| `GALLERY` | `gallery` | "Gallery control." | Array de attachments; não exemplificado |
|
||||||
|
| `STRUCTURE` | `structure` | "Structure control." | Selector visual de estrutura de colunas |
|
||||||
|
| `SELECT2` | `select2` | "Select2 control." | Multi-select com pesquisa |
|
||||||
|
| `DATE_TIME` | `date_time` | "Date/Time control." | Não exemplificado |
|
||||||
|
| `BOX_SHADOW`/`TEXT_SHADOW` | `box_shadow`/`text_shadow` | Controlos de sombra (também disponíveis como *group control*, ver 3.2) | `heading.php` usa a variante *group* (`Group_Control_Text_Shadow`), não o controlo simples directamente |
|
||||||
|
| `ANIMATION`/`HOVER_ANIMATION`/`EXIT_ANIMATION` | idem | Dropdown de animações de entrada/hover/saída | Não exemplificado |
|
||||||
|
|
||||||
|
### 3.2 Group controls (`get_groups_names()`) — controlos compostos multi-campo
|
||||||
|
|
||||||
|
`Controls_Manager::get_groups_names()` (confirmado, `includes/managers/controls.php`): `background`,
|
||||||
|
`border`, `typography`, `image-size`, `box-shadow`, `css-filter`, `text-shadow`, `flex-container`,
|
||||||
|
`grid-container`, `flex-item`, `text-stroke`. Instanciados via `Group_Control_{Nome}` (mesma
|
||||||
|
convenção de nome de classe) e adicionados com `add_group_control(Group_Control_X::get_type(), [...])`
|
||||||
|
— confirmado em uso real por `heading.php` para `typography` (`'name' => 'typography', 'global' =>
|
||||||
|
['default' => Global_Typography::TYPOGRAPHY_PRIMARY], 'selector' => '{{WRAPPER}}
|
||||||
|
.elementor-heading-title'`), `text_stroke` e `text_shadow` (ambos só `name`+`selector`).
|
||||||
|
|
||||||
|
**Nota honesta sobre o nível de detalhe desta tabela**: os parâmetros confirmados vêm de dois
|
||||||
|
sítios — (a) leitura directa do PHPDoc de cada constante em `Controls_Manager` (garante que o
|
||||||
|
tipo existe e o nome é exacto), e (b) o subconjunto de tipos efectivamente usados em
|
||||||
|
`Widget_Heading::register_controls()` (garante o shape exacto dos parâmetros aceites nesses
|
||||||
|
casos). Para os tipos **não** exemplificados em `heading.php` (ex. `MEDIA`, `REPEATER`, `WYSIWYG`,
|
||||||
|
`GALLERY`), o shape descrito é conhecimento geral do ecossistema Elementor, **não confirmado
|
||||||
|
linha-a-linha nesta sessão** por leitura do respectivo ficheiro `Control_{Nome}.php` — uma
|
||||||
|
implementação real do `list-control-types` deveria ler cada uma dessas ~35 classes individualmente
|
||||||
|
(`includes/controls/*.php`) antes de publicar o schema completo, algo fora do âmbito desta tarefa.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. `validate-widget-spec` — o que validar `[DESENHO PRÓPRIO, sobre APIs `[REAL]`]`
|
||||||
|
|
||||||
|
Lista de validações propostas, cada uma ancorada num método real do Elementor:
|
||||||
|
|
||||||
|
| # | Validação | API real usada | Comportamento em caso de falha |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 1 | `name` é um slug válido (`[a-z][a-z0-9_]*`) | `[DESENHO PRÓPRIO]` — regra nossa, sem equivalente Elementor explícito (Elementor não valida o formato do nome, só usa-o como chave de array) | Rejeita antes de qualquer outra verificação |
|
||||||
|
| 2 | `name` **não colide** com um widget já registado (nem custom nem core) | `Plugin::$instance->widgets_manager->get_widget_types( $name )` — confirmado, `includes/managers/widgets.php:356`: com argumento devolve a instância registada ou `null`; sem argumento devolve o array completo. Um `null` significa "livre" | Rejeita com `widget_name_taken` se não for `null` — **crítico**: sem este check, `add_control_to_stack()` internamente ainda protege *controlos* duplicados (ver #4), mas dois **widgets** com o mesmo `name` fariam o segundo silenciosamente substituir o primeiro no array `_widget_types[]` (última chamada a `register()` ganha, sem aviso) |
|
||||||
|
| 3 | Cada `controls[].type` está em `Controls_Manager::get_controls_names()` (secção 3.1) | `Controls_Manager::get_control($type)` — confirmado, devolve `false` se o tipo não existir (`includes/managers/controls.php`, `get_control()`) | Rejeita com `unknown_control_type`, lista os tipos válidos no erro |
|
||||||
|
| 4 | `controls[].id` únicos dentro do mesmo widget | **Confirmado directamente no código real**: `Controls_Manager::add_control_to_stack()` já faz este check em runtime — `if ( ! $options['overwrite'] && isset(...) ) { _doing_it_wrong(...'Cannot redeclare control with same name "%s"'...); return false; }` (`includes/managers/controls.php`). Um gerador que ignorasse isto produziria um widget que silenciosamente perde controlos duplicados (o segundo `add_control()` falha e devolve `false`, sem excepção) | O nosso `validate-widget-spec` replica este check **antes** da compilação, para dar ao agente um erro estruturado em vez de deixar o `_doing_it_wrong()` (que só aparece em `WP_DEBUG`) engolir o problema silenciosamente em produção |
|
||||||
|
| 5 | `category` está em `Elements_Manager::get_categories()` (secção 2) ou é uma categoria custom já registada via `elementor/elements/categories_registered` | `Elements_Manager::get_categories()` — confirmado, `includes/managers/elements.php:121` | Aviso (não bloqueia) — uma categoria desconhecida faz o Elementor simplesmente não mostrar o widget em nenhum grupo do painel (falha silenciosa da UI, não fatal); melhor avisar do que bloquear, dado que uma categoria custom pode legitimamente só existir depois de o widget ser gerado (ordem de hooks) |
|
||||||
|
| 6 | Todo `{{control_id}}` referenciado em `render_template` corresponde a um `controls[].id` declarado | `[DESENHO PRÓPRIO]` — nenhum equivalente Elementor (o `render()` de um widget core é PHP livre, escrito à mão, nunca templated) | Rejeita com `undefined_placeholder`; evita gerar um `render()` que faz `$settings['campo_inexistente']` (PHP notice em runtime, não fatal, mas widget quebrado silenciosamente) |
|
||||||
|
| 7 | `css`/`js` (se fornecidos) não contêm tags de abertura PHP | Reaproveita **directamente** a defesa já confirmada em `EMCP_Tools_Widget_Store` real (`docs/06` §3.9 / §4 "Copiar quase 1:1" item 6): `preg_replace('/<\?(?:php|=)?/i', '', $out)` | Nunca bloqueia — **sanitiza** silenciosamente (remove a tag), documentado explicitamente na resposta da ability para o agente saber que algo foi alterado |
|
||||||
|
| 8 | Se `render_template` for omitido e um `render_php` raw for fornecido em alternativa | Reaproveita o validador de 3 camadas já confirmado em `EMCP_Tools_PHP_Snippet_Validator` (`docs/06` §3.3 — parse + scan de segurança critical/warning) | Rejeita com `invalid_php`/`unsafe_php` + relatório de findings, **mesmo contrato** de resposta já usado por `validate-php-snippet` |
|
||||||
|
|
||||||
|
**Decisão de design explícita**: preferir sempre `render_template` (placeholders, sem PHP) sobre
|
||||||
|
`render_php` (PHP raw validado pelo scanner). O primeiro elimina uma classe inteira de
|
||||||
|
vulnerabilidades (não há PHP para injectar); o segundo só deveria existir como escape-hatch para
|
||||||
|
casos que o template simples não cobre (loops, condicionais complexos) — e, quando usado, herda
|
||||||
|
**exactamente** o mesmo modelo de aprovação humana descrito na secção 6.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. O compilador spec→PHP — desenho próprio `[DESENHO PRÓPRIO]`
|
||||||
|
|
||||||
|
Esta é a peça central que o EMCP Pro tem (`EMCP_Tools_Widget_Generator`) e nós não podemos ler —
|
||||||
|
tudo nesta secção é proposta própria, fundamentada apenas nos contratos reais das secções 2-3.
|
||||||
|
|
||||||
|
### 5.1 Esqueleto da classe gerada
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
// wp-content/{sandbox}/widgets/{id}/widget.php — gerado, nunca editado à mão
|
||||||
|
class EMCP_Widget_{id} extends \Elementor\Widget_Base {
|
||||||
|
|
||||||
|
public function get_name() { return 'emcp_custom_{id}'; }
|
||||||
|
public function get_title() { return '{spec.title, esc}'; }
|
||||||
|
public function get_icon() { return '{spec.icon, esc}'; }
|
||||||
|
public function get_categories() { return [ '{spec.category, esc}' ]; }
|
||||||
|
public function get_keywords() { return [ /* spec.keywords, cada um esc_attr */ ]; }
|
||||||
|
|
||||||
|
protected function register_controls() {
|
||||||
|
// por cada item de spec.sections[], nesta ordem:
|
||||||
|
$this->start_controls_section( '{section.id}', [
|
||||||
|
'label' => '{section.label, esc}',
|
||||||
|
'tab' => '{section.tab}', // Controls_Manager::TAB_CONTENT|TAB_STYLE|...
|
||||||
|
] );
|
||||||
|
// por cada spec.controls[] com controls[].section === section.id:
|
||||||
|
$this->add_control( '{control.id}', [
|
||||||
|
'label' => '{control.label, esc}',
|
||||||
|
'type' => \Elementor\Controls_Manager::{TYPE_CONST},
|
||||||
|
'default' => {control.default, json-safe},
|
||||||
|
'options' => {control.options}, // só se aplicável ao tipo
|
||||||
|
'selectors' => {control.selectors}, // só se preenchido na spec
|
||||||
|
] );
|
||||||
|
$this->end_controls_section();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function render() {
|
||||||
|
$settings = $this->get_settings_for_display();
|
||||||
|
$__out = '{spec.render_template com placeholders ainda por trocar}';
|
||||||
|
// por cada {{control_id}}: substituir por uma função de escape
|
||||||
|
// apropriada ao TIPO do controlo (ver tabela 5.2), NUNCA um output cru
|
||||||
|
echo $__out; // phpcs:ignore — já escapado por control-type na compilação
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Os nomes de classe (`EMCP_Widget_{id}`) e de widget (`emcp_custom_{id}`) **não são invenção desta
|
||||||
|
secção** — são reaproveitados **tal-qual** do padrão já confirmado em `EMCP_Tools_Widget_Store::create()`
|
||||||
|
(`docs/06` §3.9, citação directa: *"insere o post primeiro (para o ID poder semear nomes únicos de
|
||||||
|
classe `EMCP_Widget_{id}`/widget `emcp_custom_{id}`)"*) — o `id` vem do post ID do CPT
|
||||||
|
`emcp_widget` real (secção 6), garantindo unicidade sem precisar de gerar UUIDs.
|
||||||
|
|
||||||
|
### 5.2 Mapeamento tipo de controlo → função de escape no `render()` `[DESENHO PRÓPRIO]`
|
||||||
|
|
||||||
|
A parte mais sensível do compilador — cada placeholder `{{control_id}}` no `render_template` é
|
||||||
|
substituído em tempo de compilação por PHP que já aplica a escape correcta, nunca por output cru:
|
||||||
|
|
||||||
|
| Tipo de controlo | Escape aplicado no PHP gerado |
|
||||||
|
|---|---|
|
||||||
|
| `text`, `textarea`, `select`, `hidden` | `esc_html( $settings['{id}'] )` |
|
||||||
|
| `url` | `esc_url( $settings['{id}']['url'] )` (o valor é um array `{url,...}`, confirmado pelo shape usado em `heading.php` `link`) |
|
||||||
|
| `color` | `esc_attr( $settings['{id}'] )` (usado tipicamente dentro de `style="..."`) |
|
||||||
|
| `media` | `esc_url( $settings['{id}']['url'] )` para o URL; `absint( $settings['{id}']['id'] )` se usado para `wp_get_attachment_image()` |
|
||||||
|
| `wysiwyg` | `wp_kses_post( $settings['{id}'] )` — permite HTML controlado, mesma função usada por `Widget_Heading::render()` real para o campo `title` |
|
||||||
|
| `number`, `slider` | `floatval`/`intval( $settings['{id}'] )` (nunca `esc_html`, para não citar acidentalmente) |
|
||||||
|
| `repeater`, `gallery` | Não elegível para substituição directa de placeholder — requer um `foreach` no `render_template`; **fora de âmbito de um compilador v1** (ver secção 8), recomenda-se cair para `render_php` validado nesse caso |
|
||||||
|
|
||||||
|
Este mapeamento é o equivalente, do lado do gerador de widgets, à "defesa de injecção de tag PHP"
|
||||||
|
que `EMCP_Tools_Widget_Store` já aplica aos ficheiros `.css`/`.js` gerados (secção 4, item 7) — a
|
||||||
|
mesma filosofia ("o compilador nunca confia no conteúdo, escapa sempre pelo tipo declarado")
|
||||||
|
aplicada a uma superfície diferente (HTML de saída em vez de ficheiros estáticos).
|
||||||
|
|
||||||
|
### 5.3 Registo do widget compilado — reaproveita o hook real (secção 2)
|
||||||
|
|
||||||
|
O ficheiro gerado é só **definido** (via `include_once`, manifest-only — secção 6); a
|
||||||
|
**instanciação e registo** acontece no hook real `elementor/widgets/register`:
|
||||||
|
|
||||||
|
```php
|
||||||
|
add_action( 'elementor/widgets/register', function( $widgets_manager ) {
|
||||||
|
foreach ( /* manifest de widgets activos */ as $entry ) {
|
||||||
|
$class = $entry['class_name'];
|
||||||
|
if ( class_exists( $class ) ) {
|
||||||
|
$widgets_manager->register( new $class() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
```
|
||||||
|
|
||||||
|
Exactamente o padrão já confirmado em `docs/04-THEMER.md` §10 para os widgets dinâmicos do Themer
|
||||||
|
(`class-themer-widgets.php`: `require_once` tardio + registo dentro do mesmo hook).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Sandbox e ciclo de vida — reaproveitar o modelo já confirmado em código real `[REAL]`
|
||||||
|
|
||||||
|
**Esta secção é a que menos inventa.** `EMCP_Tools_Widget_Store` e `EMCP_Tools_Widget_Loader`
|
||||||
|
**existem** na build Free (confirmado por leitura integral em `docs/06-SANDBOX-CUSTOM-CODE.md`
|
||||||
|
§3.9-3.10) — só a peça que os alimenta (o compilador, secção 5) e a peça que os expõe a agentes de
|
||||||
|
IA (secção 7) é que faltam. Reaproveitamos aqui, **explicitamente citado de `docs/06`**, o modelo
|
||||||
|
de 4 camadas de segurança já validado nesta série para PHP gerado por IA (o mesmo aplicado ao
|
||||||
|
Themer PHP, `docs/04-THEMER.md` §11, e aos PHP Snippets, `docs/06` §1/§3.2-3.3):
|
||||||
|
|
||||||
|
### 6.1 Camada 1 — Manifest-only lookup `[REAL]`
|
||||||
|
|
||||||
|
`EMCP_Tools_Widget_Store::rebuild_manifest()`/`read_manifest()` (confirmado, `docs/06` §3.9)
|
||||||
|
mantêm `wp-content/emcp-sandbox/widgets-manifest.json` — array de entradas só dos widgets
|
||||||
|
`publish` (activos). `EMCP_Tools_Widget_Loader::register_widgets()` **nunca faz scan de
|
||||||
|
directório** — lê só o manifest. Idêntico ao padrão do `PHP_Snippet_Loader` (`docs/06` §3.2).
|
||||||
|
|
||||||
|
### 6.2 Camada 2 — Tamper guard sha256 `[REAL]`
|
||||||
|
|
||||||
|
Cada entrada do manifest guarda `_emcp_php_hash`/`_emcp_css_hash`/`_emcp_js_hash` (confirmado,
|
||||||
|
meta keys reais de `EMCP_Tools_Widget_Store`). Antes de `include_once`, o loader recalcula
|
||||||
|
`hash('sha256', file_get_contents($path))` e compara — um ficheiro alterado por fora do fluxo
|
||||||
|
normal (ex. edição manual FTP, ou um bug de escrita concorrente) é **ignorado silenciosamente**
|
||||||
|
em vez de executado.
|
||||||
|
|
||||||
|
### 6.3 Camada 3 — Path containment guard `[REAL]`
|
||||||
|
|
||||||
|
O mesmo `strpos(normalize(path), normalize(sandbox_dir)) === 0` já confirmado no snippet loader
|
||||||
|
(`docs/06` §3.2) aplica-se aqui — uma entrada de manifest cujo `php_path` aponte para fora de
|
||||||
|
`wp-content/emcp-sandbox/widgets/` é recusada antes de qualquer `include_once`.
|
||||||
|
|
||||||
|
### 6.4 Camada 4 — `runtime_validate()` + shutdown fatal-recovery `[REAL]`
|
||||||
|
|
||||||
|
**Peça específica de widgets, sem equivalente directo nos PHP Snippets** — confirmado em `docs/06`
|
||||||
|
§3.9: `EMCP_Tools_Widget_Store::runtime_validate()` instancia a classe gerada e chama
|
||||||
|
`$instance->get_stack()` — o mesmo método `get_stack()`/`init_controls()` confirmado nesta sessão
|
||||||
|
directamente em `widget-base.php` (secção 2) — **antes** de deixar o widget ir para produção. Se o
|
||||||
|
`register_controls()` gerado rebentar (erro de sintaxe residual não apanhado pelo
|
||||||
|
`token_get_all()` do validador de PHP, ou uma chamada a `add_control()` com tipo inválido que
|
||||||
|
escapou à validação da spec), é apanhado aqui, dentro de um contexto controlado, **em vez de dar
|
||||||
|
white-screen no painel do editor Elementor** para todos os utilizadores desse site.
|
||||||
|
`safeguard_active()` (confirmado, `docs/06` §3.9) degrada automaticamente para `draft` se esta
|
||||||
|
validação falhar — nunca deixa algo partido ficar "activo". Adicionalmente, `mark_error()` +
|
||||||
|
o shutdown handler do `EMCP_Tools_Widget_Loader` (`register_shutdown_function`, mesma rede de
|
||||||
|
segurança confirmada para snippets em `docs/06` §3.2) apanham fatais que só se manifestam já em
|
||||||
|
runtime de front-end (não apanhados por `runtime_validate()` em tempo de activação).
|
||||||
|
|
||||||
|
### 6.5 Decisão de segurança: aprovação humana obrigatória antes de activar `[DESENHO PRÓPRIO, precedente REAL]`
|
||||||
|
|
||||||
|
**Recomendação: sim, replicar a filosofia "zero tool de auto-activação" já aplicada ao Themer PHP
|
||||||
|
e aos PHP Snippets no Free.** Justificação, ambas citações directas de código real já confirmado
|
||||||
|
nesta série:
|
||||||
|
|
||||||
|
- Themer PHP (`docs/04-THEMER.md` §2, comentário de topo do ficheiro citado tal-qual): *"AI
|
||||||
|
authors + validates DRAFT PHP templates; there is intentionally no attach tool — a human selects
|
||||||
|
a template in the Themer metabox (the execution gate)"*.
|
||||||
|
- PHP Snippets (`docs/06` §1, citação directa): *"There is intentionally no 'activate' tool: a
|
||||||
|
snippet created via MCP is an inactive draft until a human administrator reviews it and activates
|
||||||
|
it in the Sandbox admin screen"*.
|
||||||
|
|
||||||
|
Ambos os subsistemas **reais** deste plugin, que geram/executam código a partir de input de IA,
|
||||||
|
escolheram consistentemente **nunca** expor a transição draft→activo como uma ability MCP — só a
|
||||||
|
UI de admin (com nonce + sessão humana) o consegue fazer. `EMCP_Tools_Widget_Store::set_status()`
|
||||||
|
(confirmado, `docs/06` §3.9, mesmo padrão de `PHP_Snippet_Store::set_status()`, §3.1) já é **esse**
|
||||||
|
portão — é chamado hoje só pelo handler AJAX do admin, nunca por uma ability.
|
||||||
|
|
||||||
|
**Tensão a resolver explicitamente**: o nome `set-widget-status` **está** no catálogo de 8 tools
|
||||||
|
da captura de ecrã do EMCP Pro (secção 0) — o que sugere que o EMCP Pro **pode** de facto expor a
|
||||||
|
activação via MCP, divergindo da filosofia "zero tool de attach" aplicada ao resto do Free. Como
|
||||||
|
não temos o código Pro para confirmar o comportamento real dessa tool (permission_callback exacto,
|
||||||
|
se força `runtime_validate()`, se pede confirmação extra), **este blueprint recomenda explicitamente
|
||||||
|
NÃO seguir esse precedente sem mais garantias**, e propõe duas alternativas concretas na secção 7
|
||||||
|
(a decisão B é a recomendada).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Tabela de abilities propostas `[DESENHO PRÓPRIO sobre camadas `[REAL]``]`
|
||||||
|
|
||||||
|
Os 8 nomes seguem exactamente o catálogo visível na UI admin do EMCP Pro (secção 0) — usados só
|
||||||
|
como naming-target. Schema/comportamento/permissões são propostas próprias, construídas em cima de
|
||||||
|
`EMCP_Tools_Widget_Store` (`[REAL]`, secção 6) e `Controls_Manager`/`Elements_Manager`/`Widgets_Manager`
|
||||||
|
(`[REAL]`, secções 2-4).
|
||||||
|
|
||||||
|
| Ability | Input (proposto) | O que faz | Permissão proposta | readonly / destructive |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| `list-control-types` | `{}` | Devolve o catálogo da secção 3 (tipos simples + group controls), com o schema de parâmetros por tipo (marcando explicitamente na resposta quais foram confirmados por uso real vs. inferidos, secção 3.2) | `edit_posts` (é só metadata de descoberta, paralelo ao `list-tools` do dispatcher, `docs/00` §4) | readonly, idempotent |
|
||||||
|
| `validate-widget-spec` | `{ spec: object }` (schema da secção 1) | Corre as 8 validações da secção 4, devolve `{valid, findings[]}` — pensado para iterar **antes** de `create-custom-widget`, mesmo contrato de UX que `validate-php-snippet` já usa (`docs/06` §1) | `manage_options` (leitura+análise, sem escrita) | readonly, idempotent |
|
||||||
|
| `create-custom-widget` | `{ spec: object }` | Valida primeiro (rejeita com o mesmo relatório de `validate-widget-spec` em caso de falha — reaproveita `normalize_write_result()`, padrão confirmado em `docs/06` §1); compila via o gerador (secção 5); grava via `EMCP_Tools_Widget_Store::create($spec, false)` — **`$active` forçado a `false`, sempre**, independentemente de qualquer flag no input | `manage_options` **E** `unfiltered_html` (mesma dupla capability já usada por `create-php-snippet`, `docs/06` §1 — justificação idêntica: gerar código PHP que corre no servidor) | write, não destructive, não idempotent |
|
||||||
|
| `update-custom-widget` | `{ widget_id: int, spec: object (parcial) }` | Re-valida a spec mesclada; recompila; se o widget já estava `active`, corre `runtime_validate()` (secção 6.4) e degrada para `draft` automaticamente em caso de falha (mesmo padrão de `PHP_Snippet_Store::update()`, `docs/06` §3.1) | `manage_options` + `unfiltered_html` | write, não idempotent |
|
||||||
|
| `get-custom-widget` | `{ widget_id: int }` | Devolve a spec completa (fonte-de-verdade `_emcp_spec`), estado (`draft`/`active`), hash do PHP compilado, e o último erro registado (`_emcp_last_error`) — todos campos reais já confirmados em `EMCP_Tools_Widget_Store` | `manage_options` | readonly, idempotent |
|
||||||
|
| `list-custom-widgets` | `{ status?: 'active'\|'draft'\|'any' }` | Lista widgets custom com nome/título/categoria/estado | `manage_options` | readonly, idempotent |
|
||||||
|
| `set-widget-status` | **Não recomendada como MCP ability** (ver decisão B abaixo) — se implementada apesar disso: `{ widget_id: int, status: 'draft' }` **apenas** (nunca `'active'`) | Só permite **desactivar** via MCP; activar continua exclusivo da UI admin | `manage_options` + `unfiltered_html` | write, não destructive (desactivar nunca perde dados — o `.php` fica em disco, só sai do manifest) |
|
||||||
|
| `delete-custom-widget` | `{ widget_id: int, force?: bool }` | Apaga o post CPT + os ficheiros do widget (`widget.php`/`style.css`/`script.js`) + reconstrói o manifest — mesmo padrão de `PHP_Snippet_Store::delete()` (`docs/06` §3.1) | `manage_options` + `unfiltered_html` | **destructive**, idempotent |
|
||||||
|
|
||||||
|
**Decisão explícita sobre `set-widget-status`** (retoma a tensão da secção 6.5):
|
||||||
|
|
||||||
|
- **Opção A (seguir o naming do catálogo Pro)**: `set-widget-status` aceita `'active'` e `'draft'`
|
||||||
|
via MCP, com `runtime_validate()` sempre corrido antes de qualquer activação e falha bloqueante.
|
||||||
|
Mais próximo do que o EMCP Pro parece expor (só pelo nome).
|
||||||
|
- **Opção B — recomendada**: `set-widget-status` só aceita `'draft'` via MCP (desactivação, sempre
|
||||||
|
segura e reversível). Activar um widget (`'active'`) **nunca** é alcançável por uma ability —
|
||||||
|
só pelo mesmo botão de admin que já existe para `EMCP_Tools_Widget_Store` hoje. Justificação:
|
||||||
|
consistência com os **dois** outros subsistemas reais de código-gerado-por-IA deste plugin
|
||||||
|
(Themer PHP e PHP Snippets), ambos deliberadamente sem essa capacidade via MCP; e o facto de a
|
||||||
|
spec ser JSON estruturado (não PHP livre) **não elimina** o risco — um `render_template` pode
|
||||||
|
ainda produzir HTML/CSS malicioso se a validação da secção 4 tiver um buraco, e um agente com
|
||||||
|
acesso MCP total (`manage_options`+`unfiltered_html`) que consiga também activar sem revisão
|
||||||
|
humana teria, de facto, execução arbitrária de HTML/CSS no site — a mesma classe de risco que
|
||||||
|
motivou a decisão "zero tool de attach" nos outros dois subsistemas.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Blueprint para réplica
|
||||||
|
|
||||||
|
### Copiar quase 1:1 (já é código real, `[REAL]`)
|
||||||
|
|
||||||
|
1. **`EMCP_Tools_Widget_Store` + `EMCP_Tools_Widget_Loader` inteiros** — manifest-only, tamper
|
||||||
|
guard sha256, path containment, `runtime_validate()`, `safeguard_active()`, `mark_error()`,
|
||||||
|
shutdown handler. É a peça mais valiosa de todo este blueprint precisamente porque **já não é
|
||||||
|
desenho** — é código já testado em produção no Free (confirmado nesta série).
|
||||||
|
2. **O hook real de registo** (`elementor/widgets/register` → `Widgets_Manager::register()`) — não
|
||||||
|
há nada a inventar aqui, é a única forma suportada do Elementor.
|
||||||
|
3. **A defesa de injecção de tag PHP** em assets `.css`/`.js` (`preg_replace('/<\?(?:php|=)?/i',
|
||||||
|
'', $out)`) — já confirmada e reaproveitável tal-qual.
|
||||||
|
4. **O validador de PHP de 3 camadas** (`EMCP_Tools_PHP_Snippet_Validator`) — reaproveitar
|
||||||
|
directamente para o caminho de escape-hatch `render_php` (secção 4, item 8).
|
||||||
|
|
||||||
|
### Construir de raiz (`[DESENHO PRÓPRIO]`, sequência recomendada por dependência)
|
||||||
|
|
||||||
|
1. **`list-control-types`** primeiro — zero risco, zero dependência de escrita, e é o que
|
||||||
|
`validate-widget-spec` e o compilador (passo 3) vão consumir para saber o que é válido. Nesta
|
||||||
|
fase, ler efectivamente os ~35 ficheiros `includes/controls/Control_*.php` individuais para
|
||||||
|
fechar a lacuna identificada na secção 3.2 (parâmetros não confirmados por leitura directa).
|
||||||
|
2. **`validate-widget-spec`** — as 8 verificações da secção 4, todas independentes entre si,
|
||||||
|
implementáveis e testáveis isoladamente antes de qualquer geração de PHP.
|
||||||
|
3. **O compilador** — começar **só** com os controlos de conteúdo mais simples (`text`,
|
||||||
|
`textarea`, `select`, `switcher`, `url`, `color`, `number`) e o mapeamento de escape da secção
|
||||||
|
5.2 para esses tipos; adicionar `media` a seguir (precisa de `wp_get_attachment_image()`/
|
||||||
|
`esc_url()`); deixar os *group controls* (`typography`/`background`/`border`, secção 3.2) para
|
||||||
|
uma segunda iteração — têm parâmetros (`name`+`selector`+`fields_options`) mais complexos que
|
||||||
|
os controlos simples e não são necessários para um MVP funcional.
|
||||||
|
4. **`create`/`get`/`list`/`update-custom-widget`** sobre o `Widget_Store` já copiado (passo 0) —
|
||||||
|
sempre `$active = false` forçado em `create`, nunca negociável via input.
|
||||||
|
5. **`delete-custom-widget`** — trivial depois do passo 4.
|
||||||
|
6. **`set-widget-status`** — implementar a Opção B da secção 7 (só desactivação via MCP); a
|
||||||
|
activação continua um botão de admin humano ligado directamente a `set_status('active')` do
|
||||||
|
`Widget_Store` já existente.
|
||||||
|
|
||||||
|
### Deixar de fora ou adiar explicitamente
|
||||||
|
|
||||||
|
- **`content_template()`** (preview Backbone.js no editor sem round-trip ao servidor) — um widget
|
||||||
|
gerado sem isto ainda funciona perfeitamente (o Elementor faz fallback a um pedido AJAX de
|
||||||
|
`render()` PHP no editor), só perde fluidez de preview em tempo real. Não é bloqueante para v1.
|
||||||
|
- **`repeater`/`gallery`** no compilador de `render_template` (secção 5.2) — exigem lógica de
|
||||||
|
loop que um simples find-and-replace de placeholders não cobre; ficam atrás do escape-hatch
|
||||||
|
`render_php` validado até haver um motor de template mais rico (ex. Mustache-like `{{#each}}`).
|
||||||
|
- **Skins** (`Widget_Base::register_skins()`/`add_skin()`) — mecanismo real e confirmado, mas de
|
||||||
|
complexidade desproporcional face ao valor para um gerador orientado a spec simples; um widget
|
||||||
|
gerado não precisa de múltiplos skins visuais geridos por IA.
|
||||||
|
- **`get_upsale_data()`/promoções Pro** — específico do próprio Elementor Core anunciar o
|
||||||
|
Elementor Pro; irrelevante para widgets gerados por um sistema terceiro.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Fonte
|
||||||
|
|
||||||
|
**Elementor 4.2.2 (Free), lido directamente nesta sessão via
|
||||||
|
`ssh://server/home/ealmeida/emanuelalmeida.pt/wp-content/plugins/elementor/`:**
|
||||||
|
|
||||||
|
- `includes/base/widget-base.php` (integral — `Widget_Base`, incl. `get_stack()`, `init_controls()`,
|
||||||
|
`register_skin_control()`, `render_content()`, `get_initial_config()`, `add_deprecation_message()`)
|
||||||
|
- `includes/managers/controls.php` (integral — `Controls_Manager`, todas as constantes de tipo
|
||||||
|
simples e de grupo, `get_controls_names()`, `get_groups_names()`, `register_controls()`,
|
||||||
|
`add_control_to_stack()` incl. o check de duplicado `_doing_it_wrong`, `register()`, `get_control()`)
|
||||||
|
- `includes/managers/widgets.php` (excertos: `register(Widget_Base $widget_instance)` linha 261,
|
||||||
|
`get_widget_types($widget_name = null)` linha 356 lido na íntegra, hook `elementor/widgets/register`
|
||||||
|
linha 159)
|
||||||
|
- `includes/managers/elements.php` (excertos: `get_categories()`/`add_category()`, `init_categories()`
|
||||||
|
lido na íntegra com a lista completa de categorias nativas desta versão)
|
||||||
|
- `includes/widgets/heading.php` (integral — `Widget_Heading`, exemplo real e completo de
|
||||||
|
`register_controls()`/`render()`/`content_template()`, usado como fonte dos parâmetros
|
||||||
|
confirmados por uso real na tabela da secção 3.1)
|
||||||
|
|
||||||
|
**Cruzado com esta mesma série de documentos** (código real do `emcp-tools`, já lido em sessões
|
||||||
|
anteriores, não relido nesta tarefa):
|
||||||
|
|
||||||
|
- `docs/06-SANDBOX-CUSTOM-CODE.md` §1, §3.1-3.3, §3.9-3.10, §4 — `EMCP_Tools_PHP_Snippet_Abilities`,
|
||||||
|
`EMCP_Tools_PHP_Snippet_Store`, `EMCP_Tools_PHP_Snippet_Loader`, `EMCP_Tools_PHP_Snippet_Validator`,
|
||||||
|
**e criticamente `EMCP_Tools_Widget_Store`/`EMCP_Tools_Widget_Loader`** (confirmados presentes na
|
||||||
|
build Free, ao contrário de `EMCP_Tools_Widget_Generator`/`EMCP_Tools_Widget_Builder_Abilities`,
|
||||||
|
confirmados ausentes)
|
||||||
|
- `docs/04-THEMER.md` §2, §11 — `EMCP_Tools_Themer_PHP_Abilities`, `EMCP_Tools_Themer_PHP_Store`, e
|
||||||
|
o hook real `elementor/widgets/register` já usado por `class-themer-widgets.php`/
|
||||||
|
`class-themer-widget-classes.php` (widgets Elementor dinâmicos do próprio Themer, base real
|
||||||
|
adicional confirmando o contrato da secção 2)
|
||||||
|
- `docs/00-ARQUITECTURA.md` §5 — `normalize_result()`/`normalize_write_result()`, padrão de
|
||||||
|
envelope de resposta reaproveitado nas tabelas de ability da secção 7
|
||||||
|
- `docs/11-WOOCOMMERCE-BLUEPRINT.md` — usado como referência de **estilo e formato** deste tipo de
|
||||||
|
documento "blueprint, não auditoria" (secção 0 explícita, tabelas de ability propostas com
|
||||||
|
coluna de risco/destructive, secção final "Blueprint para réplica" com copiar/construir/omitir)
|
||||||
|
|
||||||
|
**Não lido nesta tarefa (lacunas explícitas, ver secção 3.2 e 8):** os ~35 ficheiros individuais
|
||||||
|
`includes/controls/Control_*.php` e os 11 ficheiros `includes/controls/groups/Group_Control_*.php`
|
||||||
|
do Elementor (schema exacto por tipo de controlo, além dos exemplificados em `heading.php`);
|
||||||
|
qualquer código do EMCP Pro (`EMCP_Tools_Widget_Generator`, `EMCP_Tools_Widget_Builder_Abilities`)
|
||||||
|
— **fisicamente ausente da árvore Free instalada**, confirmado por tentativa de leitura em sessão
|
||||||
|
anterior desta série (`docs/06` §3.9), não retentado nesta tarefa.
|
||||||
+10
-7
@@ -1,11 +1,12 @@
|
|||||||
# EMCP Tools — Mapeamento completo e blueprint de réplica
|
# EMCP Tools — Mapeamento completo e blueprint de réplica
|
||||||
|
|
||||||
Índice e síntese de 12 documentos (~6 940 linhas), produzidos 19-08-2026 por leitura directa do
|
Índice e síntese de 13 documentos (~7 810 linhas), produzidos 19-08-2026 por leitura directa do
|
||||||
código-fonte `emcp-tools` v3.12.1 (build Free, `msrbuilds/elementor-mcp`, GPL-2.0-or-later),
|
código-fonte `emcp-tools` v3.12.1 (build Free, `msrbuilds/elementor-mcp`, GPL-2.0-or-later),
|
||||||
instalado em `emanuelalmeida.pt` (`/home/ealmeida/emanuelalmeida.pt/wp-content/plugins/emcp-tools/`),
|
instalado em `emanuelalmeida.pt` (`/home/ealmeida/emanuelalmeida.pt/wp-content/plugins/emcp-tools/`),
|
||||||
mais um doc adicional (11) fundamentado no WooCommerce 11.0.1 real instalado em
|
mais dois docs adicionais (11, WooCommerce; 12, Widget Builder) fundamentados noutro código real
|
||||||
`ecommerce.descomplicar.pt`. Cruzado com `skill://emcp-tools` (auditoria de postura de segurança
|
quando o EMCP Pro correspondente está ausente. Cruzado com `skill://emcp-tools` (auditoria de
|
||||||
viva nos 3 sites do ecossistema: `descomplicar.pt`, `emanuelalmeida.pt`, `starter.descomplicar.pt`).
|
postura de segurança viva nos 3 sites do ecossistema: `descomplicar.pt`, `emanuelalmeida.pt`,
|
||||||
|
`starter.descomplicar.pt`).
|
||||||
|
|
||||||
**Objectivo desta série:** especificação funcional completa, ao nível de "o que este código faz e
|
**Objectivo desta série:** especificação funcional completa, ao nível de "o que este código faz e
|
||||||
porquê", para servir de base a uma réplica própria — seja fork (GPL permite) seja reescrita limpa.
|
porquê", para servir de base a uma réplica própria — seja fork (GPL permite) seja reescrita limpa.
|
||||||
@@ -31,10 +32,12 @@ copiar-1:1 / simplificar / deixar de fora.
|
|||||||
| [09-STOCK-IMAGES-CLOUD-OAUTH](09-STOCK-IMAGES-CLOUD-OAUTH.md) | Unsplash/Pexels/Pixabay, EMCP Cloud (ligação/sync), servidor OAuth para MCP remoto | 654 | ~17 | Nenhuma |
|
| [09-STOCK-IMAGES-CLOUD-OAUTH](09-STOCK-IMAGES-CLOUD-OAUTH.md) | Unsplash/Pexels/Pixabay, EMCP Cloud (ligação/sync), servidor OAuth para MCP remoto | 654 | ~17 | Nenhuma |
|
||||||
| [10-MODULOS-E-INVENTARIO-PRO](10-MODULOS-E-INVENTARIO-PRO.md) | Sistema de módulos (toggles), Image Optimization, SVG Support, **inventário definitivo das 30 classes Pro-only** | 661 | n/a + 30 ausentes | — (estrutural + auditoria) |
|
| [10-MODULOS-E-INVENTARIO-PRO](10-MODULOS-E-INVENTARIO-PRO.md) | Sistema de módulos (toggles), Image Optimization, SVG Support, **inventário definitivo das 30 classes Pro-only** | 661 | n/a + 30 ausentes | — (estrutural + auditoria) |
|
||||||
| [11-WOOCOMMERCE-BLUEPRINT](11-WOOCOMMERCE-BLUEPRINT.md) | **Não é auditoria EMCP** — blueprint de abilities WooCommerce (produtos/encomendas/clientes/cupões/analytics/webhooks) fundamentado no WooCommerce real, para preencher o gap `EMCP_Tools_Woo_Integration` (Pro, código ausente). Exclui SEO/Yoast por pedido. | 878 | ~35 propostas | WooCommerce activo |
|
| [11-WOOCOMMERCE-BLUEPRINT](11-WOOCOMMERCE-BLUEPRINT.md) | **Não é auditoria EMCP** — blueprint de abilities WooCommerce (produtos/encomendas/clientes/cupões/analytics/webhooks) fundamentado no WooCommerce real, para preencher o gap `EMCP_Tools_Woo_Integration` (Pro, código ausente). Exclui SEO/Yoast por pedido. | 878 | ~35 propostas | WooCommerce activo |
|
||||||
|
| [12-WIDGET-BUILDER-BLUEPRINT](12-WIDGET-BUILDER-BLUEPRINT.md) | **Não é auditoria EMCP** — blueprint de gerador spec→widget-Elementor-PHP (marca `[REAL]`/`[DESENHO PRÓPRIO]` em cada afirmação), fundamentado na API pública `Widget_Base`/`Controls_Manager` do Elementor real + no `EMCP_Tools_Widget_Store`/`Widget_Loader` já Free/reais (doc 06). Recomenda "zero tool de activação via MCP", ecoando Themer PHP/Sandbox. | 651 | 8 propostas | Elementor activo |
|
||||||
|
|
||||||
Total: **~6 940 linhas de documentação**, ~165 abilities EMCP confirmadas + ~35 abilities WooCommerce
|
Total: **~7 810 linhas de documentação**, ~165 abilities EMCP confirmadas + ~35 WooCommerce + 8
|
||||||
propostas (doc 11, não implementadas no EMCP — blueprint próprio) sobre **~128 465 linhas de PHP**
|
Widget Builder propostas (docs 11-12, não implementadas no EMCP — blueprints próprios) sobre
|
||||||
do plugin `emcp-tools` (`~50k` das quais são o SDK Freemius bundled, irrelevante para réplica).
|
**~128 465 linhas de PHP** do plugin `emcp-tools` (`~50k` das quais são o SDK Freemius bundled,
|
||||||
|
irrelevante para réplica).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"0": "EMCP Tools — Mapeamento completo e blueprint de réplica",
|
||||||
|
"1": "5. Sequência de construção recomendada para uma réplica",
|
||||||
|
"2": "11 — WooCommerce: blueprint de abilities MCP (não é auditoria EMCP)",
|
||||||
|
"3": "3. Encomendas",
|
||||||
|
"4": "6. Relatórios / Analytics (`wc-analytics/*`)",
|
||||||
|
"5": "7. Webhooks",
|
||||||
|
"6": "2. Produtos",
|
||||||
|
"7": "4. Clientes",
|
||||||
|
"8": "5. Cupões / Descontos",
|
||||||
|
"9": "9. Blueprint para réplica"
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
.
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
# Graph Report - emcp-tools-mapping (2026-08-19)
|
||||||
|
|
||||||
|
## Corpus Check
|
||||||
|
- 13 files · ~66,158 words
|
||||||
|
- Verdict: corpus is large enough that graph structure adds value.
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
- 55 nodes · 54 edges · 10 communities
|
||||||
|
- Extraction: 100% EXTRACTED · 0% INFERRED · 0% AMBIGUOUS
|
||||||
|
- Token cost: 0 input · 0 output
|
||||||
|
|
||||||
|
## Graph Freshness
|
||||||
|
- Built from commit: `2f98a527`
|
||||||
|
- Run `git rev-parse HEAD` and compare to check if the graph is stale.
|
||||||
|
- Run `graphify update .` after code changes (no API cost).
|
||||||
|
|
||||||
|
## Community Hubs (Navigation)
|
||||||
|
- [[_COMMUNITY_EMCP Tools — Mapeamento completo e blueprint de réplica|EMCP Tools — Mapeamento completo e blueprint de réplica]]
|
||||||
|
- [[_COMMUNITY_5. Sequência de construção recomendada para uma réplica|5. Sequência de construção recomendada para uma réplica]]
|
||||||
|
- [[_COMMUNITY_11 — WooCommerce blueprint de abilities MCP (não é auditoria EMCP)|11 — WooCommerce: blueprint de abilities MCP (não é auditoria EMCP)]]
|
||||||
|
- [[_COMMUNITY_3. Encomendas|3. Encomendas]]
|
||||||
|
- [[_COMMUNITY_6. Relatórios Analytics (`wc-analytics`)|6. Relatórios / Analytics (`wc-analytics/*`)]]
|
||||||
|
- [[_COMMUNITY_7. Webhooks|7. Webhooks]]
|
||||||
|
- [[_COMMUNITY_2. Produtos|2. Produtos]]
|
||||||
|
- [[_COMMUNITY_4. Clientes|4. Clientes]]
|
||||||
|
- [[_COMMUNITY_5. Cupões Descontos|5. Cupões / Descontos]]
|
||||||
|
- [[_COMMUNITY_9. Blueprint para réplica|9. Blueprint para réplica]]
|
||||||
|
|
||||||
|
## God Nodes (most connected - your core abstractions)
|
||||||
|
1. `11 — WooCommerce: blueprint de abilities MCP (não é auditoria EMCP)` - 12 edges
|
||||||
|
2. `EMCP Tools — Mapeamento completo e blueprint de réplica` - 9 edges
|
||||||
|
3. `5. Sequência de construção recomendada para uma réplica` - 8 edges
|
||||||
|
4. `3. Encomendas` - 6 edges
|
||||||
|
5. `6. Relatórios / Analytics (`wc-analytics/*`)` - 5 edges
|
||||||
|
6. `7. Webhooks` - 5 edges
|
||||||
|
7. `2. Produtos` - 4 edges
|
||||||
|
8. `4. Clientes` - 4 edges
|
||||||
|
9. `5. Cupões / Descontos` - 4 edges
|
||||||
|
10. `9. Blueprint para réplica` - 4 edges
|
||||||
|
|
||||||
|
## Surprising Connections (you probably didn't know these)
|
||||||
|
- None detected - all connections are within the same source files.
|
||||||
|
|
||||||
|
## Import Cycles
|
||||||
|
- None detected.
|
||||||
|
|
||||||
|
## Communities (10 total, 0 thin omitted)
|
||||||
|
|
||||||
|
### Community 0 - "EMCP Tools — Mapeamento completo e blueprint de réplica"
|
||||||
|
Cohesion: 0.25
|
||||||
|
Nodes (8): 1. Mapa dos documentos, 2. O achado mais importante de toda a série, 3. Os 5 subsistemas de maior valor para copiar quase verbatim, 4. Padrões arquitecturais que atravessam todo o plugin, 6. Tabela de risco consolidada, 7. Como usar esta documentação, EMCP Tools — Mapeamento completo e blueprint de réplica, Metodologia
|
||||||
|
|
||||||
|
### Community 1 - "5. Sequência de construção recomendada para uma réplica"
|
||||||
|
Cohesion: 0.25
|
||||||
|
Nodes (8): 5. Sequência de construção recomendada para uma réplica, Explicitamente fora de âmbito (doc 10 §2), Fase 0 — Fundação (sem isto, nada mais tem rollback nem é seguro), Fase 1 — Conteúdo WordPress puro (sem dependência de Elementor), Fase 2 — Elementor (o núcleo de "construir páginas"), Fase 3 — Camadas de produto sobre o conteúdo, Fase 4 — Superfície de risco elevado, opt-in, Fase 5 — Integrações e periferia (valor incremental, não bloqueante)
|
||||||
|
|
||||||
|
### Community 2 - "11 — WooCommerce: blueprint de abilities MCP (não é auditoria EMCP)"
|
||||||
|
Cohesion: 0.29
|
||||||
|
Nodes (5): 0. Porque este documento é diferente dos outros 10, 10. Fonte, 11 — WooCommerce: blueprint de abilities MCP (não é auditoria EMCP), 1. Panorama, 8. Segurança / gating recomendado
|
||||||
|
|
||||||
|
### Community 3 - "3. Encomendas"
|
||||||
|
Cohesion: 0.33
|
||||||
|
Nodes (6): 3.1 Estados reais (não inventados — via `wc_get_order_statuses()`), 3.2 Itens da encomenda e ciclo de escrita, 3.3 Devoluções (refunds), 3.4 Notas da encomenda, 3.5 Tabela de ability proposta — Encomendas, 3. Encomendas
|
||||||
|
|
||||||
|
### Community 4 - "6. Relatórios / Analytics (`wc-analytics/*`)"
|
||||||
|
Cohesion: 0.40
|
||||||
|
Nodes (5): 6.1 Dois grupos de controllers, 6.2 Capability real para relatórios (não `manage_woocommerce`), 6.3 Data stores de agregação confirmados, 6.4 Tabela de ability proposta — Analytics, 6. Relatórios / Analytics (`wc-analytics/*`)
|
||||||
|
|
||||||
|
### Community 5 - "7. Webhooks"
|
||||||
|
Cohesion: 0.40
|
||||||
|
Nodes (5): 7.1 Modelo de dados real (confirmado por `$data` default em `WC_Webhook`), 7.2 Validação de topic — não é livre texto, 7.3 Entrega assíncrona por omissão, 7.4 Tabela de ability proposta — Webhooks, 7. Webhooks
|
||||||
|
|
||||||
|
### Community 6 - "2. Produtos"
|
||||||
|
Cohesion: 0.50
|
||||||
|
Nodes (4): 2.1 Schema real (campos confirmados por leitura de `get_item_schema()`), 2.2 Endpoints extra confirmados (além do CRUD standard), 2.3 Tabela de ability proposta — Produtos, 2. Produtos
|
||||||
|
|
||||||
|
### Community 7 - "4. Clientes"
|
||||||
|
Cohesion: 0.50
|
||||||
|
Nodes (4): 4.1 Capabilities reais (não `manage_woocommerce` genérico), 4.2 Schema real (confirmado por `get_item_schema()`), 4.3 Tabela de ability proposta — Clientes, 4. Clientes
|
||||||
|
|
||||||
|
### Community 8 - "5. Cupões / Descontos"
|
||||||
|
Cohesion: 0.50
|
||||||
|
Nodes (4): 5.1 Tipos de desconto reais (via `wc_get_coupon_types()`), 5.2 Campos de restrição confirmados (schema/data props do coupon), 5.3 Tabela de ability proposta — Cupões, 5. Cupões / Descontos
|
||||||
|
|
||||||
|
### Community 9 - "9. Blueprint para réplica"
|
||||||
|
Cohesion: 0.50
|
||||||
|
Nodes (4): 9.1 Prioridade de construção (por valor/esforço, assumindo Fase 0 do doc00 já feita), 9.2 Reaproveitamento explícito dos padrões já documentados, 9.3 O que NÃO replicar sem decisão explícita, 9. Blueprint para réplica
|
||||||
|
|
||||||
|
## Knowledge Gaps
|
||||||
|
- **43 isolated node(s):** `0. Porque este documento é diferente dos outros 10`, `1. Panorama`, `2.1 Schema real (campos confirmados por leitura de `get_item_schema()`)`, `2.2 Endpoints extra confirmados (além do CRUD standard)`, `2.3 Tabela de ability proposta — Produtos` (+38 more)
|
||||||
|
These have ≤1 connection - possible missing edges or undocumented components.
|
||||||
|
|
||||||
|
## Suggested Questions
|
||||||
|
_Questions this graph is uniquely positioned to answer:_
|
||||||
|
|
||||||
|
- **Why does `11 — WooCommerce: blueprint de abilities MCP (não é auditoria EMCP)` connect `11 — WooCommerce: blueprint de abilities MCP (não é auditoria EMCP)` to `3. Encomendas`, `6. Relatórios / Analytics (`wc-analytics/*`)`, `7. Webhooks`, `2. Produtos`, `4. Clientes`, `5. Cupões / Descontos`, `9. Blueprint para réplica`?**
|
||||||
|
_High betweenness centrality (0.852) - this node is a cross-community bridge._
|
||||||
|
- **Why does `EMCP Tools — Mapeamento completo e blueprint de réplica` connect `EMCP Tools — Mapeamento completo e blueprint de réplica` to `5. Sequência de construção recomendada para uma réplica`, `11 — WooCommerce: blueprint de abilities MCP (não é auditoria EMCP)`?**
|
||||||
|
_High betweenness centrality (0.463) - this node is a cross-community bridge._
|
||||||
|
- **Why does `5. Sequência de construção recomendada para uma réplica` connect `5. Sequência de construção recomendada para uma réplica` to `EMCP Tools — Mapeamento completo e blueprint de réplica`?**
|
||||||
|
_High betweenness centrality (0.245) - this node is a cross-community bridge._
|
||||||
|
- **What connects `0. Porque este documento é diferente dos outros 10`, `1. Panorama`, `2.1 Schema real (campos confirmados por leitura de `get_item_schema()`)` to the rest of the system?**
|
||||||
|
_43 weakly-connected nodes found - possible documentation gaps or missing edges._
|
||||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
{"/media/ealmeida/Dados/Dev/emcp-tools-mapping/docs/11-WOOCOMMERCE-BLUEPRINT.md":{"size":49610,"mtime_ns":1787119782238708136,"hash":"9fd04f9130e82a256c669972c07eed11c97a82dbb0a05703f99c467702c67601"},"/media/ealmeida/Dados/Dev/emcp-tools-mapping/docs/INDEX.md":{"size":17826,"mtime_ns":1787119806433982191,"hash":"75376ed5bec049c735c11a41cf8c0a4dfe472c35624a0242eeac261222f52bcf"}}
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,67 @@
|
|||||||
|
{
|
||||||
|
"docs/00-ARQUITECTURA.md": {
|
||||||
|
"mtime": 1787116450.5336444,
|
||||||
|
"ast_hash": "ae2257c149d3430da6823558b3750ed6",
|
||||||
|
"semantic_hash": ""
|
||||||
|
},
|
||||||
|
"docs/01-ELEMENTOR-CLASSICO.md": {
|
||||||
|
"mtime": 1787117462.0784547,
|
||||||
|
"ast_hash": "0a293f4d65574952b8be56fa57259af6",
|
||||||
|
"semantic_hash": ""
|
||||||
|
},
|
||||||
|
"docs/02-ATOMIC-V4-GUTENBERG.md": {
|
||||||
|
"mtime": 1787117548.949143,
|
||||||
|
"ast_hash": "b0f909b8212428e433c1116688a23763",
|
||||||
|
"semantic_hash": ""
|
||||||
|
},
|
||||||
|
"docs/03-WORDPRESS-CORE-TEMAS.md": {
|
||||||
|
"mtime": 1787117302.2605903,
|
||||||
|
"ast_hash": "4930718faa985078263df3adc0d476cf",
|
||||||
|
"semantic_hash": ""
|
||||||
|
},
|
||||||
|
"docs/04-THEMER.md": {
|
||||||
|
"mtime": 1787117386.2796364,
|
||||||
|
"ast_hash": "caa9c0b7a5fa8f9fbba4c6843970e5d6",
|
||||||
|
"semantic_hash": ""
|
||||||
|
},
|
||||||
|
"docs/05-REDIRECTS-SEARCH-LEDGER.md": {
|
||||||
|
"mtime": 1787117265.8808794,
|
||||||
|
"ast_hash": "dda4a02913bdc0e1a4ad183a94cfaa94",
|
||||||
|
"semantic_hash": ""
|
||||||
|
},
|
||||||
|
"docs/06-SANDBOX-CUSTOM-CODE.md": {
|
||||||
|
"mtime": 1787117373.3797464,
|
||||||
|
"ast_hash": "dabc5ce243ae7f9dd3affd5e443f0689",
|
||||||
|
"semantic_hash": ""
|
||||||
|
},
|
||||||
|
"docs/07-SYSTEM-OPS.md": {
|
||||||
|
"mtime": 1787117916.9961705,
|
||||||
|
"ast_hash": "7c3c342bc505c620d58b3fd7a2a4bea4",
|
||||||
|
"semantic_hash": ""
|
||||||
|
},
|
||||||
|
"docs/08-INTEGRACOES-TERCEIROS.md": {
|
||||||
|
"mtime": 1787117238.7386587,
|
||||||
|
"ast_hash": "6554b848b88618e823cbe45c7cde149c",
|
||||||
|
"semantic_hash": ""
|
||||||
|
},
|
||||||
|
"docs/09-STOCK-IMAGES-CLOUD-OAUTH.md": {
|
||||||
|
"mtime": 1787117293.6521044,
|
||||||
|
"ast_hash": "a7cc5fc5d8dadcff115a5ecd7025ee30",
|
||||||
|
"semantic_hash": ""
|
||||||
|
},
|
||||||
|
"docs/10-MODULOS-E-INVENTARIO-PRO.md": {
|
||||||
|
"mtime": 1787117721.9560692,
|
||||||
|
"ast_hash": "d44d5afadee4312373d78c989a74cc1d",
|
||||||
|
"semantic_hash": ""
|
||||||
|
},
|
||||||
|
"docs/11-WOOCOMMERCE-BLUEPRINT.md": {
|
||||||
|
"mtime": 1787119782.238708,
|
||||||
|
"ast_hash": "14dd4613a906c0de9e1c64d5e8706822",
|
||||||
|
"semantic_hash": ""
|
||||||
|
},
|
||||||
|
"docs/INDEX.md": {
|
||||||
|
"mtime": 1787119806.4339821,
|
||||||
|
"ast_hash": "bc362e3c65cd504567232c69a800427c",
|
||||||
|
"semantic_hash": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user