- Create PostGridShortcodeRegistrar for WordPress shortcode registration - Implement RenderPostGridUseCase following Clean Architecture - Add PostGridQueryBuilder for custom WP_Query construction - Add PostGridShortcodeRenderer for HTML/CSS generation - Register shortcode in DIContainer with proper DI - Add shortcode usage guide in post-grid admin panel - Fix sidebar layout: add hide_for_logged_in check to wrapper visibility Shortcode attributes: category, tag, author, posts_per_page, columns, show_pagination, show_thumbnail, show_excerpt, show_meta, etc. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
411 lines
14 KiB
Markdown
411 lines
14 KiB
Markdown
# Especificacion de Shortcode Post Grid
|
|
|
|
## Purpose
|
|
|
|
Crear un shortcode `[roi_post_grid]` que permita insertar grids de posts en cualquier pagina o entrada de WordPress, con filtros configurables por categoria, tag, autor y otras opciones. Sigue Clean Architecture delegando logica a Use Cases.
|
|
|
|
## Context
|
|
|
|
### Problema Actual
|
|
El componente `post-grid` implementado en `templates-unificados` solo funciona en templates de archivo (home.php, archive.php, etc.) porque depende del loop principal de WordPress (`global $wp_query`).
|
|
|
|
### Solucion
|
|
Crear un shortcode que:
|
|
1. Siga Clean Architecture (ShortcodeRegistrar → UseCase → Repository)
|
|
2. Reutilice estilos del componente `post-grid` existente
|
|
3. Permita filtrar posts por categoria, tag, autor
|
|
4. No dependa del loop principal de WordPress
|
|
|
|
### Relacion con templates-unificados
|
|
- Este shortcode **complementa** (no reemplaza) el componente post-grid
|
|
- El componente post-grid sigue funcionando en templates de archivo
|
|
- El shortcode permite insertar grids en contenido arbitrario
|
|
|
|
### Nota sobre shortcodes legacy
|
|
Los shortcodes existentes (`apu_table`, `apu_row`) estan en `Inc/apu-tables.php` (patron legacy). Este nuevo shortcode sigue el patron moderno de Clean Architecture.
|
|
|
|
---
|
|
|
|
## Requirements
|
|
|
|
### Requirement: Arquitectura del Shortcode
|
|
|
|
The shortcode MUST follow Clean Architecture patterns.
|
|
|
|
#### Scenario: Ubicacion del ShortcodeRegistrar
|
|
- **WHEN** se implementa el shortcode
|
|
- **THEN** DEBE estar en `Shared/Infrastructure/Wordpress/PostGridShortcodeRegistrar.php`
|
|
- **AND** DEBE usar namespace `ROITheme\Shared\Infrastructure\Wordpress`
|
|
- **AND** DEBE registrar el shortcode via add_shortcode
|
|
|
|
#### Scenario: Delegacion a Use Case
|
|
- **WHEN** el shortcode se ejecuta
|
|
- **THEN** PostGridShortcodeRegistrar DEBE delegar a RenderPostGridUseCase
|
|
- **AND** NO DEBE contener logica de negocio
|
|
- **AND** solo sanitiza atributos y pasa al Use Case
|
|
|
|
#### Scenario: Ubicacion del Use Case
|
|
- **WHEN** se implementa la logica del shortcode
|
|
- **THEN** DEBE estar en `Shared/Application/UseCases/RenderPostGrid/RenderPostGridUseCase.php`
|
|
- **AND** DEBE usar namespace `ROITheme\Shared\Application\UseCases\RenderPostGrid`
|
|
- **AND** DEBE orquestar Query, Renderer y Settings
|
|
|
|
#### Scenario: Ubicacion del QueryBuilder
|
|
- **WHEN** se construye el WP_Query
|
|
- **THEN** DEBE estar en `Shared/Infrastructure/Query/PostGridQueryBuilder.php`
|
|
- **AND** DEBE usar namespace `ROITheme\Shared\Infrastructure\Query`
|
|
- **AND** DEBE recibir parametros y retornar WP_Query
|
|
|
|
#### Scenario: Ubicacion del ShortcodeRenderer
|
|
- **WHEN** se genera el HTML del grid
|
|
- **THEN** DEBE estar en `Shared/Infrastructure/Ui/PostGridShortcodeRenderer.php`
|
|
- **AND** DEBE usar namespace `ROITheme\Shared\Infrastructure\Ui`
|
|
- **AND** DEBE inyectar CSSGeneratorInterface
|
|
|
|
---
|
|
|
|
### Requirement: Estructura de Clases
|
|
|
|
Each class MUST have single responsibility following SRP.
|
|
|
|
#### Scenario: Responsabilidad de PostGridShortcodeRegistrar
|
|
- **WHEN** se define PostGridShortcodeRegistrar
|
|
- **THEN** DEBE tener metodo estatico `register()` para add_shortcode
|
|
- **AND** DEBE tener metodo `handleShortcode(array $atts): string`
|
|
- **AND** handleShortcode DEBE sanitizar atributos
|
|
- **AND** handleShortcode DEBE obtener UseCase del DIContainer
|
|
- **AND** handleShortcode DEBE retornar resultado del UseCase
|
|
- **AND** NO DEBE tener mas de 50 lineas
|
|
|
|
#### Scenario: Responsabilidad de RenderPostGridUseCase
|
|
- **WHEN** se define RenderPostGridUseCase
|
|
- **THEN** DEBE recibir RenderPostGridRequest como input
|
|
- **AND** DEBE inyectar PostGridQueryBuilderInterface via constructor
|
|
- **AND** DEBE inyectar PostGridShortcodeRendererInterface via constructor
|
|
- **AND** DEBE inyectar ComponentSettingsRepositoryInterface via constructor
|
|
- **AND** DEBE orquestar: obtener settings, construir query, renderizar
|
|
- **AND** DEBE retornar string HTML
|
|
- **AND** NO DEBE tener mas de 80 lineas
|
|
|
|
#### Scenario: Responsabilidad de PostGridQueryBuilder
|
|
- **WHEN** se define PostGridQueryBuilder
|
|
- **THEN** DEBE recibir parametros de filtro (category, tag, etc.)
|
|
- **AND** DEBE construir array de argumentos WP_Query
|
|
- **AND** DEBE retornar WP_Query configurado
|
|
- **AND** NO DEBE generar HTML
|
|
- **AND** NO DEBE tener mas de 100 lineas
|
|
|
|
#### Scenario: Responsabilidad de PostGridShortcodeRenderer
|
|
- **WHEN** se define PostGridShortcodeRenderer
|
|
- **THEN** DEBE recibir WP_Query y configuracion
|
|
- **AND** DEBE inyectar CSSGeneratorInterface
|
|
- **AND** DEBE generar HTML del grid
|
|
- **AND** DEBE generar CSS inline
|
|
- **AND** NO DEBE construir queries
|
|
- **AND** NO DEBE tener mas de 150 lineas
|
|
|
|
---
|
|
|
|
### Requirement: Interfaces en Domain/Application
|
|
|
|
Interfaces MUST be defined for dependency injection.
|
|
|
|
#### Scenario: Interface del QueryBuilder
|
|
- **WHEN** se define la interface
|
|
- **THEN** DEBE estar en `Shared/Domain/Contracts/PostGridQueryBuilderInterface.php`
|
|
- **AND** DEBE tener metodo `build(array $params): \WP_Query`
|
|
|
|
#### Scenario: Interface del Renderer
|
|
- **WHEN** se define la interface
|
|
- **THEN** DEBE estar en `Shared/Domain/Contracts/PostGridShortcodeRendererInterface.php`
|
|
- **AND** DEBE tener metodo `render(\WP_Query $query, array $settings, array $options): string`
|
|
|
|
#### Scenario: Request DTO
|
|
- **WHEN** se define el DTO de entrada
|
|
- **THEN** DEBE estar en `Shared/Application/UseCases/RenderPostGrid/RenderPostGridRequest.php`
|
|
- **AND** DEBE ser readonly class con propiedades tipadas
|
|
- **AND** DEBE contener todos los atributos del shortcode
|
|
|
|
---
|
|
|
|
### Requirement: Atributos del Shortcode
|
|
|
|
The shortcode MUST accept configurable attributes.
|
|
|
|
#### Scenario: Atributo category
|
|
- **WHEN** se usa `[roi_post_grid category="precios-unitarios"]`
|
|
- **THEN** DEBE filtrar posts por slug de categoria
|
|
- **AND** DEBE aceptar multiples categorias separadas por coma
|
|
- **AND** ejemplo: `category="cat1,cat2,cat3"`
|
|
|
|
#### Scenario: Atributo exclude_category
|
|
- **WHEN** se usa `[roi_post_grid exclude_category="noticias"]`
|
|
- **THEN** DEBE excluir posts de esas categorias
|
|
- **AND** DEBE aceptar multiples categorias separadas por coma
|
|
|
|
#### Scenario: Atributo tag
|
|
- **WHEN** se usa `[roi_post_grid tag="concreto"]`
|
|
- **THEN** DEBE filtrar posts por slug de tag
|
|
- **AND** DEBE aceptar multiples tags separados por coma
|
|
|
|
#### Scenario: Atributo author
|
|
- **WHEN** se usa `[roi_post_grid author="admin"]`
|
|
- **THEN** DEBE filtrar posts por login de autor
|
|
- **AND** DEBE aceptar ID numerico o login string
|
|
|
|
#### Scenario: Atributo posts_per_page
|
|
- **WHEN** se usa `[roi_post_grid posts_per_page="6"]`
|
|
- **THEN** DEBE limitar cantidad de posts mostrados
|
|
- **AND** default DEBE ser 9
|
|
- **AND** DEBE aceptar valores entre 1 y 50
|
|
|
|
#### Scenario: Atributo columns
|
|
- **WHEN** se usa `[roi_post_grid columns="4"]`
|
|
- **THEN** DEBE definir columnas en desktop
|
|
- **AND** default DEBE ser 3
|
|
- **AND** DEBE aceptar valores 1, 2, 3 o 4
|
|
|
|
#### Scenario: Atributo orderby
|
|
- **WHEN** se usa `[roi_post_grid orderby="title"]`
|
|
- **THEN** DEBE ordenar posts por ese campo
|
|
- **AND** default DEBE ser "date"
|
|
- **AND** opciones validas: date, title, modified, rand, comment_count
|
|
|
|
#### Scenario: Atributo order
|
|
- **WHEN** se usa `[roi_post_grid order="ASC"]`
|
|
- **THEN** DEBE definir direccion del orden
|
|
- **AND** default DEBE ser "DESC"
|
|
- **AND** opciones validas: ASC, DESC
|
|
|
|
#### Scenario: Atributo show_pagination
|
|
- **WHEN** se usa `[roi_post_grid show_pagination="true"]`
|
|
- **THEN** DEBE mostrar paginacion si hay mas posts
|
|
- **AND** default DEBE ser false
|
|
|
|
#### Scenario: Atributo offset
|
|
- **WHEN** se usa `[roi_post_grid offset="3"]`
|
|
- **THEN** DEBE saltar los primeros N posts
|
|
- **AND** default DEBE ser 0
|
|
|
|
#### Scenario: Atributo exclude_posts
|
|
- **WHEN** se usa `[roi_post_grid exclude_posts="123,456"]`
|
|
- **THEN** DEBE excluir posts por ID
|
|
- **AND** DEBE aceptar IDs separados por coma
|
|
|
|
#### Scenario: Atributos de visualizacion
|
|
- **WHEN** se usan atributos de visualizacion
|
|
- **THEN** show_thumbnail default true
|
|
- **AND** show_excerpt default true
|
|
- **AND** show_meta default true
|
|
- **AND** show_categories default true
|
|
- **AND** excerpt_length default 20
|
|
|
|
#### Scenario: Atributo class
|
|
- **WHEN** se usa `[roi_post_grid class="my-custom-grid"]`
|
|
- **THEN** DEBE agregar clase CSS adicional al contenedor
|
|
|
|
#### Scenario: Atributo id para paginacion multiple
|
|
- **WHEN** se usa `[roi_post_grid id="grid-1" show_pagination="true"]`
|
|
- **THEN** DEBE usar query var unico `paged_grid-1`
|
|
- **AND** permite multiples shortcodes paginados en misma pagina
|
|
|
|
---
|
|
|
|
### Requirement: Obtencion de Settings
|
|
|
|
The shortcode MUST obtain styles from post-grid component settings.
|
|
|
|
#### Scenario: Lectura de configuracion
|
|
- **WHEN** RenderPostGridUseCase se ejecuta
|
|
- **THEN** DEBE usar ComponentSettingsRepositoryInterface
|
|
- **AND** DEBE obtener settings de componente 'post-grid'
|
|
- **AND** DEBE aplicar colores, spacing, visual_effects del componente
|
|
|
|
#### Scenario: Settings no encontrados
|
|
- **WHEN** no existen settings de post-grid en BD
|
|
- **THEN** DEBE usar valores default definidos en VisibilityDefaults
|
|
- **AND** NO DEBE fallar con error
|
|
|
|
---
|
|
|
|
### Requirement: Renderizado HTML
|
|
|
|
The shortcode MUST generate valid HTML with proper escaping.
|
|
|
|
#### Scenario: Estructura HTML del grid
|
|
- **WHEN** el shortcode renderiza
|
|
- **THEN** DEBE generar contenedor con clase `roi-post-grid-shortcode`
|
|
- **AND** DEBE generar row con clase Bootstrap `row`
|
|
- **AND** cada card DEBE tener columna responsive
|
|
|
|
#### Scenario: Clases responsive de columnas
|
|
- **WHEN** columns es 3
|
|
- **THEN** cada card DEBE tener `col-12 col-md-6 col-lg-4`
|
|
- **AND** para columns=4: `col-12 col-md-6 col-lg-3`
|
|
- **AND** para columns=2: `col-12 col-md-6`
|
|
- **AND** para columns=1: `col-12`
|
|
|
|
#### Scenario: Sin resultados
|
|
- **WHEN** el query no encuentra posts
|
|
- **THEN** DEBE mostrar mensaje "No se encontraron publicaciones"
|
|
- **AND** NO DEBE romper layout
|
|
|
|
#### Scenario: Escaping obligatorio
|
|
- **WHEN** se genera HTML
|
|
- **THEN** DEBE usar esc_html() para textos
|
|
- **AND** DEBE usar esc_attr() para atributos
|
|
- **AND** DEBE usar esc_url() para URLs
|
|
- **AND** DEBE usar wp_kses_post() para excerpts
|
|
|
|
---
|
|
|
|
### Requirement: CSS via CSSGenerator
|
|
|
|
The shortcode MUST use CSSGeneratorInterface for styles.
|
|
|
|
#### Scenario: Generacion de CSS
|
|
- **WHEN** PostGridShortcodeRenderer genera CSS
|
|
- **THEN** DEBE inyectar CSSGeneratorInterface
|
|
- **AND** DEBE usar settings de post-grid desde BD
|
|
- **AND** DEBE generar CSS inline en el shortcode
|
|
|
|
#### Scenario: Selector unico
|
|
- **WHEN** se genera CSS
|
|
- **THEN** DEBE usar selector `.roi-post-grid-shortcode`
|
|
- **AND** si se especifica id, usar `.roi-post-grid-shortcode-{id}`
|
|
- **AND** NO DEBE conflictuar con post-grid del template
|
|
|
|
---
|
|
|
|
### Requirement: Registro del Shortcode
|
|
|
|
The shortcode MUST be registered in WordPress.
|
|
|
|
#### Scenario: Registro en bootstrap
|
|
- **WHEN** WordPress carga el tema
|
|
- **THEN** functions-addon.php DEBE llamar PostGridShortcodeRegistrar::register()
|
|
- **AND** DEBE estar disponible en editor clasico y Gutenberg
|
|
|
|
#### Scenario: Metodo register estatico
|
|
- **WHEN** se llama PostGridShortcodeRegistrar::register()
|
|
- **THEN** DEBE ejecutar add_shortcode('roi_post_grid', ...)
|
|
- **AND** DEBE usar DIContainer para obtener dependencias
|
|
|
|
---
|
|
|
|
## Implementation Order
|
|
|
|
### Fase 1: Interfaces y DTOs
|
|
1. Crear `Shared/Domain/Contracts/PostGridQueryBuilderInterface.php`
|
|
2. Crear `Shared/Domain/Contracts/PostGridShortcodeRendererInterface.php`
|
|
3. Crear `Shared/Application/UseCases/RenderPostGrid/RenderPostGridRequest.php`
|
|
|
|
### Fase 2: Use Case
|
|
1. Crear `Shared/Application/UseCases/RenderPostGrid/RenderPostGridUseCase.php`
|
|
2. Implementar orquestacion de query, settings, render
|
|
|
|
### Fase 3: Infrastructure - Query
|
|
1. Crear `Shared/Infrastructure/Query/PostGridQueryBuilder.php`
|
|
2. Implementar construccion de WP_Query con todos los filtros
|
|
|
|
### Fase 4: Infrastructure - Renderer
|
|
1. Crear `Shared/Infrastructure/Ui/PostGridShortcodeRenderer.php`
|
|
2. Implementar generacion HTML y CSS
|
|
|
|
### Fase 5: Infrastructure - Registrar
|
|
1. Crear `Shared/Infrastructure/Wordpress/PostGridShortcodeRegistrar.php`
|
|
2. Implementar registro y sanitizacion de atributos
|
|
|
|
### Fase 6: Registro y DI
|
|
1. Registrar en DIContainer las implementaciones
|
|
2. Llamar register() en functions-addon.php
|
|
|
|
### Fase 7: Testing
|
|
1. Probar en pagina estatica
|
|
2. Verificar filtros por categoria, tag
|
|
3. Verificar paginacion con id unico
|
|
|
|
---
|
|
|
|
## File Structure
|
|
|
|
```
|
|
Shared/
|
|
├── Domain/
|
|
│ └── Contracts/
|
|
│ ├── PostGridQueryBuilderInterface.php
|
|
│ └── PostGridShortcodeRendererInterface.php
|
|
├── Application/
|
|
│ └── UseCases/
|
|
│ └── RenderPostGrid/
|
|
│ ├── RenderPostGridRequest.php
|
|
│ └── RenderPostGridUseCase.php
|
|
└── Infrastructure/
|
|
├── Query/
|
|
│ └── PostGridQueryBuilder.php
|
|
├── Ui/
|
|
│ └── PostGridShortcodeRenderer.php
|
|
└── Wordpress/
|
|
└── PostGridShortcodeRegistrar.php
|
|
```
|
|
|
|
---
|
|
|
|
## Examples
|
|
|
|
### Ejemplo: Grid basico
|
|
```
|
|
[roi_post_grid]
|
|
```
|
|
|
|
### Ejemplo: Posts de una categoria
|
|
```
|
|
[roi_post_grid category="precios-unitarios" posts_per_page="6"]
|
|
```
|
|
|
|
### Ejemplo: Multiples shortcodes con paginacion
|
|
```
|
|
[roi_post_grid id="grid-cursos" category="cursos" show_pagination="true"]
|
|
|
|
[roi_post_grid id="grid-tutoriales" category="tutoriales" show_pagination="true"]
|
|
```
|
|
|
|
---
|
|
|
|
## Dependencies
|
|
|
|
### Existentes (reutilizar)
|
|
- `CSSGeneratorInterface` - Para generar CSS
|
|
- `ComponentSettingsRepositoryInterface` - Para leer config de post-grid
|
|
- `DIContainer` - Para inyeccion de dependencias
|
|
- Bootstrap 5 grid system - Para layout responsive
|
|
|
|
### Nuevas (crear)
|
|
- `PostGridQueryBuilderInterface` - Contrato para query builder
|
|
- `PostGridShortcodeRendererInterface` - Contrato para renderer
|
|
- `RenderPostGridRequest` - DTO de entrada
|
|
- `RenderPostGridUseCase` - Orquestador
|
|
- `PostGridQueryBuilder` - Implementacion query
|
|
- `PostGridShortcodeRenderer` - Implementacion render
|
|
- `PostGridShortcodeRegistrar` - Registro WordPress
|
|
|
|
---
|
|
|
|
## Testing Checklist
|
|
|
|
- [ ] Shortcode renderiza sin atributos
|
|
- [ ] Filtro por categoria funciona
|
|
- [ ] Filtro por multiples categorias funciona
|
|
- [ ] Exclusion de categoria funciona
|
|
- [ ] Filtro por tag funciona
|
|
- [ ] Columnas 1, 2, 3, 4 funcionan
|
|
- [ ] Paginacion con id unico funciona
|
|
- [ ] Multiples shortcodes paginados funcionan
|
|
- [ ] CSS se aplica desde settings de post-grid
|
|
- [ ] Mensaje "sin posts" aparece cuando corresponde
|
|
- [ ] Escaping correcto en todo el HTML
|
|
- [ ] Funciona en editor clasico
|
|
- [ ] Funciona en Gutenberg
|
|
- [ ] No hay errores PHP
|
|
- [ ] Clases tienen menos de 150 lineas
|