File: /wordpress/plugins/wp-cloud-client/1.1.7/src/BusinessProfile/ProfileBlock.php
<?php
declare(strict_types=1);
namespace VPlugins\WPCloudClient\BusinessProfile;
/**
* Registers the Business Profile Gutenberg block.
*/
final class ProfileBlock {
private const BLOCK_NAME = 'business-profile-render/bpr-block';
private const SCRIPT_HANDLE = 'wpcc-business-profile-block';
/**
* Constructor.
*
* @param ProfileRepository $repository The profile data repository.
*/
public function __construct(
private readonly ProfileRepository $repository,
) {}
/**
* Register the block on WordPress init.
*
* @return void
*/
public function register(): void {
add_action( 'init', [ $this, 'registerBlock' ] );
}
/**
* Register the Gutenberg block type and localize profile data.
*
* @return void
*/
public function registerBlock(): void {
// Only register editor assets in admin context to avoid unnecessary processing on frontend.
if ( ! is_admin() ) {
return;
}
$assetFile = VPLUGINS_WPCC_PATH . 'assets/business-profile/build/gutenberg-block.asset.php';
$assets = file_exists( $assetFile ) ? require $assetFile : [
'dependencies' => [],
'version' => '',
];
$jsPath = VPLUGINS_WPCC_PATH . 'assets/business-profile/build/gutenberg-block.js';
$version = $assets['version'] ?? ( file_exists( $jsPath ) ? (string) filemtime( $jsPath ) : VPLUGINS_WPCC_VERSION );
wp_register_script(
self::SCRIPT_HANDLE,
plugins_url( 'assets/business-profile/build/gutenberg-block.js', VPLUGINS_WPCC_FILE ),
array_merge( $assets['dependencies'] ?? [], [ 'wp-blocks', 'wp-element', 'wp-components' ] ),
$version,
true,
);
$profile = $this->repository->get();
$data = null !== $profile
? $this->preprocessData( $profile )
: [ 'Company Name' => __( 'No business profile data available', 'wp-cloud-client' ) ];
wp_localize_script(
self::SCRIPT_HANDLE,
'businessProfileData',
$data,
);
register_block_type(
self::BLOCK_NAME,
[ 'editor_script' => self::SCRIPT_HANDLE ],
);
}
/**
* Preprocess profile data for JavaScript localization.
*
* Humanizes keys and sanitizes values.
*
* @param array<string, mixed> $data Raw profile data.
* @return array<string, string> Processed key-value pairs.
*/
private function preprocessData( array $data ): array {
$processed = [];
foreach ( $data as $key => $value ) {
$processedKey = ucwords( str_replace( '_', ' ', (string) $key ) );
if ( '' === $value || null === $value ) {
$processedValue = __( 'No data available', 'wp-cloud-client' );
} elseif ( is_array( $value ) ) {
$processedValue = $this->formatArrayValue( (string) $key, $value );
} else {
$processedValue = sanitize_text_field( (string) $value );
}
$processed[ $processedKey ] = $processedValue;
}
return $processed;
}
/**
* Format an array value for Gutenberg localization.
*
* @param string $key The profile key.
* @param array<mixed> $value The array value.
* @return string Formatted string representation.
*/
private function formatArrayValue( string $key, array $value ): string {
if ( 'hours_of_operation' === $key ) {
return $this->formatHoursForBlock( $value );
}
// Single-element arrays like work_number: ["+1-306-555-0100"]
if ( 1 === count( $value ) && isset( $value[0] ) && is_string( $value[0] ) ) {
return sanitize_text_field( $value[0] );
}
// Filter out nested arrays, keep only scalar values.
$flat = array_filter( $value, static fn ( mixed $v ): bool => ! is_array( $v ) );
if ( empty( $flat ) ) {
return __( 'Complex data — use shortcode for display', 'wp-cloud-client' );
}
return implode( ', ', array_map( 'sanitize_text_field', array_map( 'strval', $flat ) ) );
}
/**
* Format hours_of_operation for the Gutenberg block preview.
*
* @param array<mixed> $hours Raw hours data.
* @return string Pipe-separated summary string.
*/
private function formatHoursForBlock( array $hours ): string {
$dayOrder = [
'Sunday' => 0,
'Monday' => 1,
'Tuesday' => 2,
'Wednesday' => 3,
'Thursday' => 4,
'Friday' => 5,
'Saturday' => 6,
];
$grouped = [];
$plain = [];
foreach ( $hours as $timeBlock ) {
if ( ! is_array( $timeBlock ) ) {
$plain[] = sanitize_text_field( (string) $timeBlock );
continue;
}
$days = $timeBlock['day_of_week'] ?? [];
if ( ! is_array( $days ) || empty( $days ) ) {
continue;
}
$opens = isset( $timeBlock['opens'] ) ? sanitize_text_field( (string) $timeBlock['opens'] ) : '';
$closes = isset( $timeBlock['closes'] ) ? sanitize_text_field( (string) $timeBlock['closes'] ) : '';
$description = (string) ( $timeBlock['description'] ?? '' );
$dayKey = implode( ',', $days );
$sortOrder = $dayOrder[ $days[0] ] ?? 99;
if ( ! isset( $grouped[ $dayKey ] ) ) {
$grouped[ $dayKey ] = [
'days' => $days,
'ranges' => [],
'sort' => $sortOrder,
];
}
if ( '' !== $opens && '' !== $closes ) {
$range = "{$opens} - {$closes}";
if ( '' !== $description ) {
$range .= ' (' . sanitize_text_field( $description ) . ')';
}
$grouped[ $dayKey ]['ranges'][] = $range;
} elseif ( '' !== $description ) {
$grouped[ $dayKey ]['ranges'][] = sanitize_text_field( $description );
}
}
uasort( $grouped, static fn ( array $a, array $b ): int => ( $a['sort'] ?? 99 ) <=> ( $b['sort'] ?? 99 ) );
$result = $plain;
foreach ( $grouped as $entry ) {
$daysText = implode( ', ', $entry['days'] );
if ( ! empty( $entry['ranges'] ) ) {
$result[] = $daysText . ': ' . implode( ', ', $entry['ranges'] );
}
}
return ! empty( $result )
? implode( ' | ', $result )
: __( 'No hours available', 'wp-cloud-client' );
}
}