Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf7dc93bc4 |
@@ -148,7 +148,7 @@ Key workflows:
|
|||||||
- [docs/modular-system.md](docs/modular-system.md) — Modules, general sections, global variables
|
- [docs/modular-system.md](docs/modular-system.md) — Modules, general sections, global variables
|
||||||
- [docs/builder-fields.md](docs/builder-fields.md) — Builder field types, Acai attributes, c-form, components
|
- [docs/builder-fields.md](docs/builder-fields.md) — Builder field types, Acai attributes, c-form, components
|
||||||
- [docs/twig-filters.md](docs/twig-filters.md) — Twig filters reference (get, hook, module, queryDB, etc.)
|
- [docs/twig-filters.md](docs/twig-filters.md) — Twig filters reference (get, hook, module, queryDB, etc.)
|
||||||
- [docs/hooks-and-api.md](docs/hooks-and-api.md) — PHP hooks, CmsApi, CocoDB, record creation, custom CMS plugins (checkPoints)
|
- [docs/hooks-and-api.md](docs/hooks-and-api.md) — PHP hooks, CmsApi, CocoDB, record creation
|
||||||
- [docs/css-js-conventions.md](docs/css-js-conventions.md) — CSS/JS/Vue 3, Tailwind, BEM, native components
|
- [docs/css-js-conventions.md](docs/css-js-conventions.md) — CSS/JS/Vue 3, Tailwind, BEM, native components
|
||||||
- [docs/quick-reference.md](docs/quick-reference.md) — Cheat sheet: domain rules, field types, filters
|
- [docs/quick-reference.md](docs/quick-reference.md) — Cheat sheet: domain rules, field types, filters
|
||||||
- [docs/production-patterns.md](docs/production-patterns.md) — Real production patterns (header, zigzag, FAQ, forms)
|
- [docs/production-patterns.md](docs/production-patterns.md) — Real production patterns (header, zigzag, FAQ, forms)
|
||||||
|
|||||||
+1
-60
@@ -33,9 +33,7 @@ Regla práctica:
|
|||||||
- No uses `exit`
|
- No uses `exit`
|
||||||
- Para leer parámetros, usa `$_REQUEST[...]` o las variables ya inyectadas por el sistema
|
- Para leer parámetros, usa `$_REQUEST[...]` o las variables ya inyectadas por el sistema
|
||||||
- En hooks, usa `CmsApi::get()` o `CocoDB::get()` como primera opción
|
- En hooks, usa `CmsApi::get()` o `CocoDB::get()` como primera opción
|
||||||
- **`CocoDB::getInstance()` no existe** — llamarlo produce un fatal `Call to undefined method`. Si de
|
- No uses `CocoDB::getInstance()` salvo necesidad real muy excepcional
|
||||||
verdad necesitas BD cruda (fuera de `CmsApi`/`CocoDB`), usa `DB::getInstance()`
|
|
||||||
(`execute`/`executeS`/`fetch`/`escape`/`foundRows`), y solo como última opción
|
|
||||||
- No escribas SQL manual con `prepare()/bind_param()` salvo que no exista forma razonable de resolverlo con `CmsApi` o `CocoDB`
|
- No escribas SQL manual con `prepare()/bind_param()` salvo que no exista forma razonable de resolverlo con `CmsApi` o `CocoDB`
|
||||||
|
|
||||||
### Estructura de un Hook
|
### Estructura de un Hook
|
||||||
@@ -109,63 +107,6 @@ $mensaje = $result["message"];
|
|||||||
|
|
||||||
**Desde c-form:** Los hooks se ejecutan automáticamente al enviar el formulario si están configurados.
|
**Desde c-form:** Los hooks se ejecutan automáticamente al enviar el formulario si están configurados.
|
||||||
|
|
||||||
### Diagnóstico: el enrutado HTTP directo de hooks no funciona en Forge
|
|
||||||
|
|
||||||
`/hooks/<id>/` da **404 incluso para hooks válidos** cuando se pide por HTTP directo en un entorno
|
|
||||||
Forge — el único camino fiable es el filtro Twig `| hook` (o `<hook>`/`CmsApi.hook()`, que pasan por el
|
|
||||||
mismo motor). Si un hook "no responde" y no ves error claro, diagnostica así:
|
|
||||||
|
|
||||||
1. Crea un módulo/página temporal que solo llame al hook con `| hook` y vuelque el resultado:
|
|
||||||
```twig
|
|
||||||
{% set d = 'hooks/mihook/' | hook({param1: 100}) %}
|
|
||||||
<pre>{{ d|json_encode(1) }}</pre>
|
|
||||||
```
|
|
||||||
2. Si eso funciona pero la llamada por HTTP/JS falla, el problema está en cómo se invoca, no en el hook.
|
|
||||||
|
|
||||||
### Modelo de ejecución de los hooks (importante)
|
|
||||||
|
|
||||||
Los hooks corren vía `eval()` dentro del motor (`builder_saas/builder_functions.php`), lo que rompe dos
|
|
||||||
supuestos habituales de PHP:
|
|
||||||
|
|
||||||
- **`__DIR__` no es la carpeta del hook** — apunta al motor. Un `require_once __DIR__.'/x.php'` falla con
|
|
||||||
*"Failed opening required"*. Usa `APP_ROOT_PATH` (con fallback a `$_SERVER['DOCUMENT_ROOT']`) para
|
|
||||||
construir rutas absolutas dentro del proyecto.
|
|
||||||
- **Un hook llamado por HTTP (`CmsApi.hook()` desde JS) no recibe los parámetros como variables PHP
|
|
||||||
sueltas** — eso solo ocurre cuando se invoca por el filtro Twig `'id' | hook({...})`. Por HTTP,
|
|
||||||
`get_defined_vars()` sale vacío: lee `$_REQUEST[...]` en su lugar.
|
|
||||||
|
|
||||||
Cada uno de estos dos puntos, sin saberlo, cuesta una sesión de depuración completa (el hook "falla" con
|
|
||||||
datos correctos y sin error visible).
|
|
||||||
|
|
||||||
### Seguridad en hooks
|
|
||||||
|
|
||||||
- **`DB::escape()` devuelve el literal YA entrecomillado.** Envolverlo a mano
|
|
||||||
(`"campo = '".escape($v)."'"`) produce `''valor''` → error de sintaxis MariaDB. Para `LIKE`, escapa el
|
|
||||||
patrón completo con sus `%` incluidos. Patrón seguro para filtros que vienen del usuario: lista blanca
|
|
||||||
de campos permitidos, `(int)` para ids, y devolver a Twig solo lo necesario (p.ej. `num`), nunca el SQL
|
|
||||||
construido a mano.
|
|
||||||
- **Nunca deleguen el hashing de contraseñas en el CMS.** `create_field` no tiene tipo contraseña;
|
|
||||||
`isPasswordField:1` en un textfield **no hashea** (guarda y lee la cadena literal), y el
|
|
||||||
`editor_password` del CMS usa **SHA1** (inadecuado). En cualquier login propio, hashea en el hook con
|
|
||||||
`password_hash()` (bcrypt/argon2id) + `password_verify()`, y marca el campo `clave_hash` como
|
|
||||||
`adminOnly`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Plugins CMS personalizados (checkPoints)
|
|
||||||
|
|
||||||
Para entender **cómo despacha el CMS un plugin custom** (no confundir con hooks de módulo), lee primero
|
|
||||||
`cms/lib/plugins/index.php` **de ese proyecto** — no hace falta explorar el servidor por SSH ni el
|
|
||||||
código de `web-base`, el contrato completo está ahí:
|
|
||||||
|
|
||||||
- `addPlugins($type, &$var, $fieldtype)` despacha los checkPoints con `include` (no clases/autoload).
|
|
||||||
- Gating nativo por `adminOnly` + `isAdmin` — un checkPoint puede declararse solo-admin.
|
|
||||||
- `$var` se pasa **por referencia** — un plugin puede mutar el valor que ven los siguientes.
|
|
||||||
- Filtros `menuFilter`/`actionFilter` por tabla/action, para que un plugin solo se dispare donde aplica.
|
|
||||||
- `newFieldType` para registrar tipos de campo nuevos del builder.
|
|
||||||
- La instalación de un plugin va vía `install.sql` + `schemaPresets` — **no** `schemas/` (ese es el
|
|
||||||
sistema viejo de `seo_toolkit`, con el `install.php` comentado; no lo repliques).
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## CmsApi (PHP)
|
## CmsApi (PHP)
|
||||||
|
|||||||
@@ -1,21 +1,5 @@
|
|||||||
# MCP Tools Reference
|
# MCP Tools Reference
|
||||||
|
|
||||||
## ⚠️ `success:true` no siempre significa que la operación se aplicó
|
|
||||||
|
|
||||||
Las write-ops/DDL (`create_field`, `update_field`, `acai-write`, `acai-line-replace`,
|
|
||||||
`set_layout_field`, `update_table_metadata`, `create_table`…) pueden devolver `success:true` /
|
|
||||||
`writeCompleted:true` **aunque el cambio real (ALTER de MySQL, insert, compilación Twig) no se haya
|
|
||||||
aplicado**. `get_table_schema` refleja el registro/`.ini`, no necesariamente el estado real de MySQL.
|
|
||||||
|
|
||||||
**Verifica siempre con una operación real posterior**, nunca te fíes solo de la respuesta del tool:
|
|
||||||
- Tras escribir/editar un módulo: `compile_module` o recargar la página y comprobar que renderiza.
|
|
||||||
- Tras crear/editar un campo: hacer un insert/update real en esa tabla y confirmar que no falla.
|
|
||||||
- Tras un insert, si necesitas el id, no te fíes ciegamente de `lastId`/`insert_id` — si algo no cuadra,
|
|
||||||
relee por un campo único ya conocido.
|
|
||||||
|
|
||||||
Es más lento que confiar en la respuesta, pero el coste de no hacerlo es peor: este es el fallo más
|
|
||||||
repetido del equipo (bloqueos de insert silenciosos, módulos servidos rotos, registros fantasma).
|
|
||||||
|
|
||||||
## Quick Reference
|
## Quick Reference
|
||||||
|
|
||||||
| Tool | Categoría | Acción |
|
| Tool | Categoría | Acción |
|
||||||
@@ -29,6 +13,8 @@ repetido del equipo (bloqueos de insert silenciosos, módulos servidos rotos, re
|
|||||||
| `acai-line-replace` | Archivos | Reemplaza un bloque concreto en un archivo existente |
|
| `acai-line-replace` | Archivos | Reemplaza un bloque concreto en un archivo existente |
|
||||||
| `acai-write` | Archivos | Crea o reescribe un archivo completo. Antes de usarlo, lee la doc correspondiente según el tipo de archivo (`module-creation-guide`, `builder-fields`, `css-js-conventions`, `hooks-and-api`) |
|
| `acai-write` | Archivos | Crea o reescribe un archivo completo. Antes de usarlo, lee la doc correspondiente según el tipo de archivo (`module-creation-guide`, `builder-fields`, `css-js-conventions`, `hooks-and-api`) |
|
||||||
| `acai-delete` | Archivos | Borra un archivo del proyecto |
|
| `acai-delete` | Archivos | Borra un archivo del proyecto |
|
||||||
|
| `get_layout_field` | Layout global | Lee header/footer/CSS global/JS global (`field: header\|footer\|style\|javascript`) — NO son ficheros normales, ver [docs/modular-system.md](modular-system.md) |
|
||||||
|
| `set_layout_field` | Layout global | Escribe header/footer/CSS global/JS global — **reemplazo destructivo del contenido completo**, no hay line-replace; verificar `compile_ok` en la respuesta |
|
||||||
| `list_page_modules` | Registros | Lista módulos de una página |
|
| `list_page_modules` | Registros | Lista módulos de una página |
|
||||||
| `add_module_to_record` | Registros | Añade módulo a una página |
|
| `add_module_to_record` | Registros | Añade módulo a una página |
|
||||||
| `remove_module_from_record` | Registros | Elimina módulo de una página |
|
| `remove_module_from_record` | Registros | Elimina módulo de una página |
|
||||||
|
|||||||
@@ -89,6 +89,28 @@ Use `<set>` tag to create variables from queries:
|
|||||||
<set :featured="'productos' | get({destacado: 1}, 'orden ASC', 3)"></set>
|
<set :featured="'productos' | get({destacado: 1}, 'orden ASC', 3)"></set>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Header, footer, and site-wide style/JS — NOT regular files
|
||||||
|
|
||||||
|
`template/estandar/modulos/custom-header-twig/index-base.tpl` and the equivalent `custom-footer-twig`
|
||||||
|
one are **not editable as files** — `acai-write`/`acai-line-replace` reject them with
|
||||||
|
`"Forbidden path: ... es un artefacto generado del layout global"`. They're special general sections
|
||||||
|
(the header/footer of the **entire site**). The `.tpl` on disk is only a generated copy for fast reading
|
||||||
|
(`acai-view` works fine on it), but the real source lives in `layout.json` and is edited with the
|
||||||
|
`get_layout_field`/`set_layout_field` MCP tools (see
|
||||||
|
[docs/mcp-tools-reference.md](mcp-tools-reference.md)):
|
||||||
|
|
||||||
|
- `field: 'header'` / `field: 'footer'` — the header/footer templates themselves.
|
||||||
|
- `field: 'style'` — global CSS for the whole site.
|
||||||
|
- `field: 'javascript'` — global JS for the whole site (delegated listeners shared by every page live
|
||||||
|
here, not in any `.js` file in the repo — a `grep` of the repo for "how does X work in the header"
|
||||||
|
can find nothing even though X works fine in production).
|
||||||
|
|
||||||
|
Workflow for a header/footer/global-CSS/global-JS change: `get_layout_field({field: '...'})` → edit the
|
||||||
|
**full** string (there's no line-replace for this — `set_layout_field` is a destructive full-content
|
||||||
|
replace) → `set_layout_field({field: '...', content: ...})`. The response includes `compile_ok` —
|
||||||
|
verify it, same as any other write-op (see the reliability warning in
|
||||||
|
[docs/mcp-tools-reference.md](mcp-tools-reference.md)).
|
||||||
|
|
||||||
|
|
||||||
## Repeatable Content (multiv2)
|
## Repeatable Content (multiv2)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user