mirror of
git://f0xx.org/ac/ac-platform-php
synced 2026-07-29 02:58:38 +03:00
242 lines
7.4 KiB
PHP
242 lines
7.4 KiB
PHP
<?php
|
||
declare(strict_types=1);
|
||
|
||
/**
|
||
* Shared footer fragment for Android Cast web consoles and hub.
|
||
*
|
||
* Configure: examples/platform/footer.config.php (see footer.config.example.php).
|
||
* Usage: <?php platform_render_footer(); ?> or platform_render_footer(['use_i18n' => false]);
|
||
*
|
||
* Layout (each row omitted when empty):
|
||
* [ top — full width ]
|
||
* [ left | right ] — main row (copyright defaults on left when left unset)
|
||
* [ bottom — full width ]
|
||
*/
|
||
|
||
function platform_footer_h(mixed $value): string
|
||
{
|
||
return htmlspecialchars((string) $value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
|
||
}
|
||
|
||
/** @return array<string, mixed> */
|
||
function platform_footer_config(): array
|
||
{
|
||
static $loaded = null;
|
||
if ($loaded !== null) {
|
||
return $loaded;
|
||
}
|
||
$path = __DIR__ . '/footer.config.php';
|
||
if (!is_file($path)) {
|
||
$path = __DIR__ . '/footer.config.example.php';
|
||
}
|
||
$loaded = require $path;
|
||
return is_array($loaded) ? $loaded : [];
|
||
}
|
||
|
||
/** @param array<string, mixed> $cfg */
|
||
function platform_footer_year_end(array $cfg): int
|
||
{
|
||
if (array_key_exists('year', $cfg) && $cfg['year'] !== null) {
|
||
return (int) $cfg['year'];
|
||
}
|
||
|
||
return (int) date('Y');
|
||
}
|
||
|
||
/**
|
||
* Year label: single year or range (e.g. 2026–2026) when year_start is set.
|
||
*
|
||
* @param array<string, mixed> $cfg
|
||
*/
|
||
function platform_footer_year_label(array $cfg): string
|
||
{
|
||
$end = platform_footer_year_end($cfg);
|
||
$start = isset($cfg['year_start']) ? (int) $cfg['year_start'] : 0;
|
||
if ($start > 0) {
|
||
if (!empty($cfg['year_through_current'])) {
|
||
return $start . ' - current';
|
||
}
|
||
if ($start === $end) {
|
||
return (string) $start;
|
||
}
|
||
|
||
return $start . "\u{2013}" . $end;
|
||
}
|
||
|
||
return (string) $end;
|
||
}
|
||
|
||
/**
|
||
* Default left line: © holder[, year|year-range].
|
||
*
|
||
* @param array<string, mixed> $cfg
|
||
*/
|
||
function platform_footer_default_left(array $cfg): string
|
||
{
|
||
$symbol = trim((string) ($cfg['copyright_symbol'] ?? "\u{00A9}"));
|
||
$holderName = trim((string) ($cfg['holder_name'] ?? ''));
|
||
$holderUrl = trim((string) ($cfg['holder_url'] ?? ''));
|
||
$holder = trim((string) ($cfg['holder'] ?? ''));
|
||
$showYear = (bool) ($cfg['show_year'] ?? true);
|
||
$parts = [];
|
||
if ($symbol !== '') {
|
||
$parts[] = $symbol;
|
||
}
|
||
if ($holderName !== '') {
|
||
$parts[] = $holderName;
|
||
} elseif ($holder !== '') {
|
||
$parts[] = $holder;
|
||
}
|
||
$line = implode(' ', $parts);
|
||
if (!$showYear) {
|
||
return $line;
|
||
}
|
||
$yearLabel = platform_footer_year_label($cfg);
|
||
if ($line === '') {
|
||
return $yearLabel;
|
||
}
|
||
|
||
return $line . ', ' . $yearLabel;
|
||
}
|
||
|
||
/** Render left footer HTML (holder link when configured). */
|
||
function platform_footer_render_left_html(array $cfg, array $opts): ?string
|
||
{
|
||
$raw = platform_footer_resolve_left($cfg, $opts);
|
||
if ($raw === null) {
|
||
return null;
|
||
}
|
||
$holderName = trim((string) ($cfg['holder_name'] ?? ''));
|
||
$holderUrl = trim((string) ($cfg['holder_url'] ?? ''));
|
||
if (platform_footer_field($cfg, $opts, 'left') === null
|
||
&& $holderName !== '' && $holderUrl !== '') {
|
||
$text = platform_footer_h($raw);
|
||
$name = platform_footer_h($holderName);
|
||
$link = '<a href="' . platform_footer_h($holderUrl) . '">' . $name . '</a>';
|
||
if (str_contains($text, $name)) {
|
||
return substr_replace($text, $link, strpos($text, $name), strlen($name));
|
||
}
|
||
return $link . ($text !== '' ? ' ' . $text : '');
|
||
}
|
||
|
||
return platform_footer_h($raw);
|
||
}
|
||
|
||
/** True when default left uses holder_name + holder_url (skip i18n text replacement). */
|
||
function platform_footer_left_has_link(array $cfg, array $opts): bool
|
||
{
|
||
if (platform_footer_field($cfg, $opts, 'left') !== null) {
|
||
return false;
|
||
}
|
||
$holderName = trim((string) ($cfg['holder_name'] ?? ''));
|
||
$holderUrl = trim((string) ($cfg['holder_url'] ?? ''));
|
||
|
||
return $holderName !== '' && $holderUrl !== '';
|
||
}
|
||
|
||
/**
|
||
* Resolve optional row text (null/empty = omit row). Supports per-render overrides in $opts.
|
||
*
|
||
* @param array<string, mixed> $cfg
|
||
* @param array<string, mixed> $opts
|
||
*/
|
||
function platform_footer_field(array $cfg, array $opts, string $key): ?string
|
||
{
|
||
if (array_key_exists($key, $opts)) {
|
||
$v = $opts[$key];
|
||
if ($v === null || $v === false) {
|
||
return null;
|
||
}
|
||
$t = trim((string) $v);
|
||
|
||
return $t === '' ? null : $t;
|
||
}
|
||
if (!array_key_exists($key, $cfg)) {
|
||
return null;
|
||
}
|
||
$v = $cfg[$key];
|
||
if ($v === null || $v === false) {
|
||
return null;
|
||
}
|
||
$t = trim((string) $v);
|
||
|
||
return $t === '' ? null : $t;
|
||
}
|
||
|
||
/**
|
||
* @param array<string, mixed> $cfg
|
||
* @param array<string, mixed> $opts
|
||
*/
|
||
function platform_footer_resolve_left(array $cfg, array $opts): ?string
|
||
{
|
||
$custom = platform_footer_field($cfg, $opts, 'left');
|
||
if ($custom !== null) {
|
||
return $custom;
|
||
}
|
||
$built = platform_footer_default_left($cfg);
|
||
|
||
return $built === '' ? null : $built;
|
||
}
|
||
|
||
/** @deprecated Use platform_footer_default_left(); kept for scripts/tests. */
|
||
function platform_footer_plain_text(array $cfg, int $year): string
|
||
{
|
||
$cfg = array_merge($cfg, ['year' => $year]);
|
||
|
||
return platform_footer_default_left($cfg);
|
||
}
|
||
|
||
/**
|
||
* @param array{use_i18n?: bool, extra_class?: string, top?: ?string, left?: ?string, right?: ?string, bottom?: ?string} $opts
|
||
*/
|
||
function platform_render_footer(array $opts = []): void
|
||
{
|
||
$cfg = platform_footer_config();
|
||
$top = platform_footer_field($cfg, $opts, 'top');
|
||
$left = platform_footer_resolve_left($cfg, $opts);
|
||
$right = platform_footer_field($cfg, $opts, 'right');
|
||
$bottom = platform_footer_field($cfg, $opts, 'bottom');
|
||
|
||
$baseClass = (string) ($opts['extra_class'] ?? ($cfg['footer_class'] ?? 'bottom-bar'));
|
||
$classes = trim($baseClass . ' platform-footer');
|
||
if ($top === null && $bottom === null && $right === null) {
|
||
$classes .= ' platform-footer--single-row';
|
||
}
|
||
|
||
$useI18n = $opts['use_i18n'] ?? (bool) ($cfg['use_i18n'] ?? true);
|
||
$i18nKey = (string) ($cfg['i18n_key'] ?? 'footer.copyright');
|
||
$leftUsesDefault = platform_footer_field($cfg, $opts, 'left') === null;
|
||
$leftHasLink = platform_footer_left_has_link($cfg, $opts);
|
||
$yearEnd = platform_footer_year_end($cfg);
|
||
$leftHtml = platform_footer_render_left_html($cfg, $opts);
|
||
|
||
echo '<footer class="' . platform_footer_h($classes) . '">';
|
||
|
||
if ($top !== null) {
|
||
echo '<div class="platform-footer__top">' . platform_footer_h($top) . '</div>';
|
||
}
|
||
|
||
if ($left !== null || $right !== null) {
|
||
echo '<div class="platform-footer__main">';
|
||
if ($left !== null) {
|
||
echo '<div class="platform-footer__left"';
|
||
if ($useI18n && $i18nKey !== '' && $leftUsesDefault && !$leftHasLink) {
|
||
echo ' data-i18n="' . platform_footer_h($i18nKey) . '"'
|
||
. ' data-year="' . platform_footer_h(platform_footer_year_label($cfg)) . '"'
|
||
. ' data-year-end="' . $yearEnd . '"';
|
||
}
|
||
echo '>' . ($leftHtml ?? platform_footer_h($left)) . '</div>';
|
||
}
|
||
if ($right !== null) {
|
||
echo '<div class="platform-footer__right">' . platform_footer_h($right) . '</div>';
|
||
}
|
||
echo '</div>';
|
||
}
|
||
|
||
if ($bottom !== null) {
|
||
echo '<div class="platform-footer__bottom">' . platform_footer_h($bottom) . '</div>';
|
||
}
|
||
|
||
echo '</footer>';
|
||
}
|