feat: initial ACRIB WordPress deployment

- WordPress 6.9.4 (es_ES) with Kadence theme
- Homepage: Hero, La Asociación, Pilares, Beneficios, Eventos, Miembros, Hazte Miembro, Contacto
- Brand identity: #13294b navy, #a12932 burgundy, #c69c48 gold
- Fonts: Raleway (headings) + Source Sans 3 (body) + Lato (UI)
- Plugins: Kadence Blocks, Polylang, Contact Form 7
- Custom CSS with full brand styling and responsive layout
- HTTPS enforced via wp-config.php proxy detection
This commit is contained in:
Malin
2026-05-19 19:25:59 +02:00
commit f3ff7b7186
6119 changed files with 1984255 additions and 0 deletions

View File

@@ -0,0 +1,135 @@
<?php declare( strict_types=1 );
namespace KadenceWP\KadenceBlocks\Optimizer\Store;
use KadenceWP\KadenceBlocks\Optimizer\Path\Path;
use KadenceWP\KadenceBlocks\Optimizer\Response\WebsiteAnalysis;
use KadenceWP\KadenceBlocks\Optimizer\Store\Contracts\Store;
/**
* A Store Decorator that caches the results of the underlying concrete
* store using the object cache.
*/
final class Cached_Store_Decorator implements Contracts\Store {
/**
* The cache group.
*/
public const GROUP = 'kb_optimizer';
/**
* The cache time to live in seconds.
*/
public const TTL = 43200;
/**
* The underlying store concrete.
*
* @var Store
*/
private Store $store;
public function __construct( Store $store ) {
$this->store = $store;
}
/**
* Whether a Path has optimization data.
*
* @param Path $path The path object.
*
* @return bool
*/
public function has( Path $path ): bool {
$cached = wp_cache_get( $this->get_has_key( $path ), self::GROUP, false, $found );
if ( $found ) {
return $cached;
}
$has = $this->store->has( $path );
wp_cache_set( $this->get_has_key( $path ), $has, self::GROUP, self::TTL );
return $has;
}
/**
* Only return the optimization if the analysis data is newer than the post's last modified
* date.
*
* @param Path $path The path object associated with the stored data.
*
* @return WebsiteAnalysis|null
*/
public function get( Path $path ): ?WebsiteAnalysis {
$cached = wp_cache_get( $this->get_key( $path ), self::GROUP );
if ( $cached instanceof WebsiteAnalysis ) {
return $cached;
}
$analysis = $this->store->get( $path );
if ( $analysis ) {
wp_cache_set( $this->get_key( $path ), $analysis, self::GROUP, self::TTL );
wp_cache_set( $this->get_has_key( $path ), true, self::GROUP, self::TTL );
}
return $analysis;
}
/**
* Set the optimization data for a post.
*
* @param Path $path The path object associated with the stored data.
* @param WebsiteAnalysis $analysis The website analysis data.
*
* @return bool
*/
public function set( Path $path, WebsiteAnalysis $analysis ): bool {
$result = $this->store->set( $path, $analysis );
if ( $result ) {
wp_cache_set( $this->get_key( $path ), $analysis, self::GROUP, self::TTL );
}
return $result;
}
/**
* Delete the optimization data for a post.
*
* @param Path $path The path object associated with the stored data.
*
* @return bool
*/
public function delete( Path $path ): bool {
wp_cache_delete( $this->get_key( $path ), self::GROUP );
wp_cache_delete( $this->get_has_key( $path ), self::GROUP );
return $this->store->delete( $path );
}
/**
* Build a cache key.
*
* @param Path $path The path object.
*
* @return string
*/
public function get_key( Path $path ): string {
return sprintf( 'kb_optimizer_url_%s', $path->hash() );
}
/**
* Get the cache key for if optimization data exists.
*
* @param Path $path The path object.
*
* @return string
*/
public function get_has_key( Path $path ): string {
return sprintf( '%s_%s', $this->get_key( $path ), '_has' );
}
}

View File

@@ -0,0 +1,46 @@
<?php declare( strict_types=1 );
namespace KadenceWP\KadenceBlocks\Optimizer\Store\Contracts;
use KadenceWP\KadenceBlocks\Optimizer\Path\Path;
use KadenceWP\KadenceBlocks\Optimizer\Response\WebsiteAnalysis;
interface Store {
/**
* Whether a Path has optimization data.
*
* @param Path $path The path object.
*
* @return bool
*/
public function has( Path $path ): bool;
/**
* Get the optimization data for a post.
*
* @param Path $path The path object.
*
* @return WebsiteAnalysis|null
*/
public function get( Path $path ): ?WebsiteAnalysis;
/**
* Set the optimization data for a post.
*
* @param Path $path The path object.
* @param WebsiteAnalysis $analysis The website analysis data.
*
* @return bool
*/
public function set( Path $path, WebsiteAnalysis $analysis ): bool;
/**
* Delete the optimization data for a post.
*
* @param Path $path The path object.
*
* @return bool
*/
public function delete( Path $path ): bool;
}

View File

@@ -0,0 +1,117 @@
<?php declare( strict_types=1 );
namespace KadenceWP\KadenceBlocks\Optimizer\Store;
use KadenceWP\KadenceBlocks\Optimizer\Path\Path;
use KadenceWP\KadenceBlocks\Optimizer\Response\WebsiteAnalysis;
use KadenceWP\KadenceBlocks\Optimizer\Status\Status;
use KadenceWP\KadenceBlocks\Optimizer\Store\Contracts\Store;
use KadenceWP\KadenceBlocks\Psr\Log\LoggerInterface;
/**
* Store decorator that blocks optimization operations on excluded posts.
*
* Excluded posts are user-controlled and should never have optimization data.
* This decorator provides infrastructure-level protection.
*
* Responsibility: Prevent optimization of excluded posts.
*/
final class Excluded_Store_Decorator implements Store {
private Store $store;
private Status $status;
private LoggerInterface $logger;
public function __construct(
Store $store,
Status $status,
LoggerInterface $logger
) {
$this->store = $store;
$this->status = $status;
$this->logger = $logger;
}
/**
* An excluded post should return false for if it has data,
* even if it does.
*
* @param Path $path The path object.
*
* @return bool
*/
public function has( Path $path ): bool {
$post_id = $path->post_id();
if ( $post_id && $this->status->is_excluded( $post_id ) ) {
return false;
}
return $this->store->has( $path );
}
/**
* Get the optimization data.
*
* @param Path $path The path object associated with the stored data.
*
* @return WebsiteAnalysis|null
*/
public function get( Path $path ): ?WebsiteAnalysis {
return $this->store->get( $path );
}
/**
* Block optimization of excluded posts.
*
* If post is excluded, silently fail and log.
* This provides defense-in-depth even if application layer checks fail.
*
* @param Path $path The path object.
* @param WebsiteAnalysis $analysis The website analysis data.
*
* @return bool
*/
public function set( Path $path, WebsiteAnalysis $analysis ): bool {
$post_id = $path->post_id();
if ( ! $post_id ) {
$this->logger->warning(
'Missing post_id in Path: Skipping exclusion check and storing optimization data without post_id',
[ 'path' => $path->path() ]
);
return $this->store->set( $path, $analysis );
}
// Check if post is excluded.
if ( $this->status->is_excluded( $post_id ) ) {
// Silently block - excluded posts should never be optimized.
$this->logger->debug(
'Blocked optimization of excluded post',
[
'post_id' => $post_id,
'path' => $path->path(),
]
);
return false;
}
return $this->store->set( $path, $analysis );
}
/**
* Allow deletion even for excluded posts.
*
* Clearing optimization data is safe for excluded posts.
* Status_Sync_Store_Decorator will preserve the excluded status meta.
*
* @param Path $path The path object.
*
* @return bool
*/
public function delete( Path $path ): bool {
return $this->store->delete( $path );
}
}

View File

@@ -0,0 +1,76 @@
<?php declare( strict_types=1 );
namespace KadenceWP\KadenceBlocks\Optimizer\Store;
use KadenceWP\KadenceBlocks\Optimizer\Path\Path;
use KadenceWP\KadenceBlocks\Optimizer\Response\WebsiteAnalysis;
use KadenceWP\KadenceBlocks\Optimizer\Store\Contracts\Store;
/**
* A Store Decorator that checks if the data is expired before
* returning it.
*/
final class Expired_Store_Decorator implements Contracts\Store {
/**
* The underlying store concrete.
*
* @var Store
*/
private Store $store;
public function __construct( Store $store ) {
$this->store = $store;
}
/**
* Whether a Path has optimization data, stale or not.
*
* @param Path $path The path object.
*
* @return bool
*/
public function has( Path $path ): bool {
return $this->store->has( $path );
}
/**
* Only return the optimization if the analysis data is newer than the post's last modified date.
*
* @param Path $path The path object associated with the stored data.
*
* @return WebsiteAnalysis|null
*/
public function get( Path $path ): ?WebsiteAnalysis {
$analysis = $this->store->get( $path );
if ( ! $analysis ) {
return null;
}
return $analysis->isStale ? null : $analysis;
}
/**
* Set the optimization data for a post.
*
* @param Path $path The path object associated with the stored data.
* @param WebsiteAnalysis $analysis The website analysis data.
*
* @return bool
*/
public function set( Path $path, WebsiteAnalysis $analysis ): bool {
return $this->store->set( $path, $analysis );
}
/**
* Delete the optimization data for a post.
*
* @param Path $path The path object associated with the stored data.
*
* @return bool
*/
public function delete( Path $path ): bool {
return $this->store->delete( $path );
}
}

View File

@@ -0,0 +1,36 @@
<?php declare( strict_types=1 );
namespace KadenceWP\KadenceBlocks\Optimizer\Store;
use KadenceWP\KadenceBlocks\Optimizer\Store\Cached_Store_Decorator;
use KadenceWP\KadenceBlocks\Optimizer\Store\Contracts\Store;
use KadenceWP\KadenceBlocks\Optimizer\Store\Excluded_Store_Decorator;
use KadenceWP\KadenceBlocks\Optimizer\Store\Expired_Store_Decorator;
use KadenceWP\KadenceBlocks\Optimizer\Store\Status_Sync_Store_Decorator;
use KadenceWP\KadenceBlocks\Optimizer\Store\Table_Store;
use KadenceWP\KadenceBlocks\StellarWP\ProphecyMonorepo\Container\Contracts\Provider as Provider_Contract;
final class Provider extends Provider_Contract {
public function register(): void {
// Decorator chain (bottom-up execution, each wraps the one below):
// When calling Store methods, execution flows TOP to BOTTOM through decorators.
//
// Reads (get): Expired → Cache → Excluded → Status_Sync → Table
// Writes (set): Expired → Cache → Excluded (blocks excluded) → Status_Sync (pure) → Table
//
// Critical ordering:
// 1. Expired MUST be outermost to filter stale data even from cache.
// 2. Excluded MUST be before Status_Sync so sync logic stays pure.
$this->container->bindDecorators(
Store::class,
[
Expired_Store_Decorator::class,
Cached_Store_Decorator::class,
Excluded_Store_Decorator::class,
Status_Sync_Store_Decorator::class,
Table_Store::class,
]
);
}
}

View File

@@ -0,0 +1,132 @@
<?php declare( strict_types=1 );
namespace KadenceWP\KadenceBlocks\Optimizer\Store;
use KadenceWP\KadenceBlocks\Optimizer\Path\Path;
use KadenceWP\KadenceBlocks\Optimizer\Response\WebsiteAnalysis;
use KadenceWP\KadenceBlocks\Optimizer\Status\Status;
use KadenceWP\KadenceBlocks\Optimizer\Store\Contracts\Store;
use WP_Post;
/**
* Store decorator that automatically synchronizes Status post meta.
*
* Pure synchronization logic - no business rules or exclusion checks.
* Exclusion is handled by Excluded_Store_Decorator upstream.
*
* Responsibility: Keep status meta in sync with optimization data.
*
* Benefits:
* - Single source of truth for optimization state
* - Impossible to forget to update status meta
* - Automatic synchronization on all write operations
* - No overhead on read operations
*/
final class Status_Sync_Store_Decorator implements Store {
private Store $store;
private Status $status;
public function __construct( Store $store, Status $status ) {
$this->store = $store;
$this->status = $status;
}
/**
* Whether a Path has optimization data.
*
* @param Path $path The path object.
*
* @return bool
*/
public function has( Path $path ): bool {
return $this->store->has( $path );
}
/**
* Get the optimization data.
*
* @param Path $path The path object associated with the stored data.
*
* @return WebsiteAnalysis|null
*/
public function get( Path $path ): ?WebsiteAnalysis {
return $this->store->get( $path );
}
/**
* Set optimization data and sync status meta.
*
* Pure sync logic - no exclusion checks needed!
* Excluded_Store_Decorator upstream prevents optimization of excluded posts.
*
* @param Path $path The path object.
* @param WebsiteAnalysis $analysis The website analysis data.
*
* @return bool
*/
public function set( Path $path, WebsiteAnalysis $analysis ): bool {
$result = $this->store->set( $path, $analysis );
if ( ! $result ) {
return false;
}
$post_id = $this->resolve_post_id( $path );
if ( $post_id ) {
$this->status->sync_from_analysis( $post_id, $analysis );
}
return true;
}
/**
* Delete optimization data and sync status meta.
*
* Pure cleanup logic - no exclusion checks needed!
*
* Note: Status::delete() will preserve excluded status as a safety measure.
*
* @param Path $path The path object.
*
* @return bool
*/
public function delete( Path $path ): bool {
$post_id = $this->resolve_post_id( $path );
$result = $this->store->delete( $path );
if ( $post_id ) {
$this->status->delete( $post_id );
}
return $result;
}
/**
* Resolve post ID from path.
*
* Priority order:
* 1. Explicit post_id from Path object (most reliable)
* 2. Current request context via get_post() and $post global
*
* @param Path $path The path object.
*
* @return int|null
*/
private function resolve_post_id( Path $path ): ?int {
// Use explicit post_id if provided (most reliable).
if ( $path->post_id() ) {
return $path->post_id();
}
// Try to get from current request context via get_post().
$current_post = get_post();
if ( ! $current_post instanceof WP_Post ) {
return null;
}
return $current_post->ID;
}
}

View File

@@ -0,0 +1,101 @@
<?php declare( strict_types=1 );
namespace KadenceWP\KadenceBlocks\Optimizer\Store;
use KadenceWP\KadenceBlocks\Optimizer\Database\Optimizer_Query;
use KadenceWP\KadenceBlocks\Optimizer\Path\Path;
use KadenceWP\KadenceBlocks\Optimizer\Response\WebsiteAnalysis;
use Throwable;
/**
* Optimizer table storage.
*/
final class Table_Store implements Contracts\Store {
private Optimizer_Query $q;
public function __construct( Optimizer_Query $query ) {
$this->q = $query;
}
/**
* Whether a Path has optimization data.
*
* @param Path $path The path object.
*
* @return bool
*/
public function has( Path $path ): bool {
return (bool) $this->q->get_var(
$this->q->qb()->select( '1' )
->where( 'path_hash', $path->hash() )
->limit( 1 )->getSQL()
);
}
/**
* Get the optimization data for a path object.
*
* @param Path $path The path object associated with the stored data.
*
* @return WebsiteAnalysis|null
*/
public function get( Path $path ): ?WebsiteAnalysis {
$json = $this->q->get_var(
$this->q->qb()->select( 'analysis' )
->where( 'path_hash', $path->hash() )
->getSQL()
);
if ( ! $json ) {
return null;
}
try {
return WebsiteAnalysis::from( json_decode( $json, true ) );
} catch ( Throwable $e ) {
return null;
}
}
/**
* Set the optimization data for a path object.
*
* @param Path $path The path object associated with the stored data.
* @param WebsiteAnalysis $analysis The website analysis data.
*
* @return bool
*/
public function set( Path $path, WebsiteAnalysis $analysis ): bool {
$json = wp_json_encode( $analysis->toArray(), JSON_UNESCAPED_SLASHES );
return false !== $this->q->qb()->upsert(
[
'path_hash' => $path->hash(),
'path' => $path->path(),
'analysis' => $json,
],
[
'path_hash',
],
[
'%s',
'%s',
'%s',
]
);
}
/**
* Delete the optimization data for a path object.
*
* @param Path $path The path object associated with the stored data.
*
* @return bool
*/
public function delete( Path $path ): bool {
return (bool) $this->q->qb()
->where( 'path_hash', $path->hash() )
->delete();
}
}