- Clarify module hook vs file hook: when to use each, different call URLs - Add schema path (cms/data/schema/) to reglas importantes - Add assets/ and hook.php to module file structure - Fix variable assignment example: use string syntax for get() - Rename twig-filters.md → twig-reference.md (covers more than filters) - Move Global Variables from modular-system.md to twig-reference.md - Update all references in CLAUDE.md Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5.1 KiB
Acai Modular System
Modules
Modules are the visual building blocks of Acai websites. Each module lives in template/estandar/modulos/<module-id>/.
File Structure
<module-id>/
├── index-base.tpl # Source template (EDIT THIS)
├── index.tpl # Compiled output (auto-generated, do NOT edit)
├── index-twig.tpl # Compiled Twig output (auto-generated, do NOT edit)
├── builder.json # Compiled builder vars (auto-generated, do NOT edit)
├── style.css # Module-scoped styles
├── script.js # Module JavaScript
├── hook.php # Module hook (optional — only if this module needs server-side logic)
├── assets/ # Vue components and other JS assets for this module
└── minified/
├── script-{hash}.js # JS minificado (servido al browser, do NOT edit)
└── style-{hash}.css # CSS minificado (servido al browser, do NOT edit)
Template Syntax
Templates use a hybrid of Twig and Acai attributes. The source file is always index-base.tpl.
<section class="hero-section" id="{{ section_id }}">
<div class="container mx-auto px-4">
<h2 data-field-type="headfield" class="text-3xl font-bold">
Title here
</h2>
<p data-field-type="textbox" class="text-lg text-gray-600">
Description text
</p>
<img data-field-type="upload" src="placeholder.jpg" class="w-full rounded-lg" />
<a data-field-type="link" href="#" class="btn">Call to action</a>
</div>
</section>
Including Modules from Other Modules
<module_id :param1="value1" :param2="'string value'"></module_id>
Parameters are received as variables inside the included module.
Global Variables
See twig-reference.md — Global Variables for the full list of variables available in all templates.
General Sections
General sections are database-backed templates used for record views, headers, footers, and reusable layouts. They use the same template engine as modules.
Key Differences from Modules
- Access record data via the
thisrecordvariable - Upload fields return arrays:
thisrecord.image[0].urlPath - Additional upload metadata:
info1(alt text),info2,info3,info4 - Foreign key field names match the schema exactly (may or may not have
_numsuffix — always check the.ini.php) - Saved via
save_general_section()(notsave_module()) - Parser type 2 = Twig (recommended), 0 = Acai legacy syntax
Example: Record Template
<article class="product-card">
<img src="{{ thisrecord.imagen[0].urlPath }}"
alt="{{ thisrecord.imagen[0].info1 }}"
class="w-full h-64 object-cover" />
<h3 class="text-xl font-semibold">{{ thisrecord.nombre }}</h3>
<p class="text-gray-600">{{ thisrecord.descripcion | raw }}</p>
<span class="text-2xl font-bold">{{ thisrecord.precio }}€</span>
</article>
Variable Assignment
Use <set> tag to create variables from queries:
<set :categories="'categorias' | get()"></set>
<set :featured="'productos' | get('destacado=1', 'orden ASC', 3)"></set>
Repeatable Content (multiv2)
The multiv2 builder field type creates repeatable groups of fields:
<div c-for="item in record.items">
<h3 data-field-type="textfield">{{ item.title }}</h3>
<p data-field-type="textbox">{{ item.description }}</p>
<img data-field-type="upload" src="{{ item.image }}" />
</div>
Access individual items: record.items[0].title, record.items[1].image, etc.
Workflow Local (Docker)
Al editar módulos en desarrollo local con Docker, los archivos compilados no se regeneran automáticamente. Hay que copiar manualmente:
# 1. Editar HTML y JS por separado
vim modulos/MODULE_ID/index-base.tpl # Solo HTML
vim modulos/MODULE_ID/script.js # Todo el JS
# 2. Copiar para que Docker los sirva
cp modulos/MODULE_ID/index-base.tpl modulos/MODULE_ID/index.tpl
cp modulos/MODULE_ID/script.js modulos/MODULE_ID/minified/script.js
Herramientas de debug
?compiletwig— Añadir a cualquier URL. Regenera losindex-twig.tplpero con un pipeline diferente al auto-compile. Útil para forzar recompilación, pero puede romper en ciertos contextos. Usar con precaución.?pruebas— Bypass de modo mantenimiento. Añadir a cualquier URL establece$_SESSION["pruebas"]=true. Solo hay que hacerlo una vez por sesión.
General Sections — Deploy
Las general sections se identifican como custom-{nombre_tabla} (ej: custom-productos). Se despliegan con save_general_section, NO con save_module:
table: nombre de la tabla (ej:"productos")content: HTML del templatejavascript: JScss: CSS
Páginas CMS (Apartados)
Campo controlador obligatorio para builder
Si una página debe renderizar módulos del builder (drag-and-drop), necesita estos campos configurados:
controlador = "cms/lib/plugins/builder_saas/controlador.php"
precontrolador = "cms/lib/plugins/builder_saas/controlador_tabla.php"
Sin estos campos, la página muestra solo la general section (custom-apartados) en vez de los módulos asignados.