311 lines
9.6 KiB
PHP
311 lines
9.6 KiB
PHP
<?php
|
|
|
|
if (!function_exists('theme')) {
|
|
/**
|
|
* Get theme configuration or specific theme property
|
|
*
|
|
* @param string|null $key Optional key to get specific theme property
|
|
* @param mixed $default Default value if key is not found
|
|
* @return mixed
|
|
*/
|
|
function theme($key = null, $default = null)
|
|
{
|
|
$activeTheme = config('themes.active', 'timebank_cc');
|
|
$themes = config('themes.themes', []);
|
|
|
|
if (!isset($themes[$activeTheme])) {
|
|
$activeTheme = 'timebank_cc'; // fallback to default
|
|
}
|
|
|
|
$themeConfig = $themes[$activeTheme] ?? [];
|
|
|
|
if ($key === null) {
|
|
return array_merge(['id' => $activeTheme], $themeConfig);
|
|
}
|
|
|
|
return data_get($themeConfig, $key, $default);
|
|
}
|
|
}
|
|
|
|
if (!function_exists('theme_name')) {
|
|
/**
|
|
* Get the current theme name
|
|
*
|
|
* @return string
|
|
*/
|
|
function theme_name()
|
|
{
|
|
return theme('name', 'Timebank.cc');
|
|
}
|
|
}
|
|
|
|
if (!function_exists('theme_id')) {
|
|
/**
|
|
* Get the current theme ID/key
|
|
*
|
|
* @return string
|
|
*/
|
|
function theme_id()
|
|
{
|
|
return config('themes.active', 'timebank_cc');
|
|
}
|
|
}
|
|
|
|
if (!function_exists('theme_color')) {
|
|
/**
|
|
* Get a theme color value
|
|
*
|
|
* @param string $colorKey Color key (e.g., 'primary.500', 'accent', 'text.primary')
|
|
* @param string|null $default Default color value
|
|
* @return string
|
|
*/
|
|
function theme_color($colorKey, $default = null)
|
|
{
|
|
return theme("colors.{$colorKey}", $default);
|
|
}
|
|
}
|
|
|
|
if (!function_exists('theme_font')) {
|
|
/**
|
|
* Get a theme typography value
|
|
*
|
|
* @param string $fontKey Font key (e.g., 'font_family_body', 'font_size_base')
|
|
* @param string|null $default Default font value
|
|
* @return string
|
|
*/
|
|
function theme_font($fontKey, $default = null)
|
|
{
|
|
return theme("typography.{$fontKey}", $default);
|
|
}
|
|
}
|
|
|
|
if (!function_exists('is_theme')) {
|
|
/**
|
|
* Check if the current theme matches the given theme ID
|
|
*
|
|
* @param string $themeId Theme ID to check against
|
|
* @return bool
|
|
*/
|
|
function is_theme($themeId)
|
|
{
|
|
return theme_id() === $themeId;
|
|
}
|
|
}
|
|
|
|
if (!function_exists('theme_logo')) {
|
|
/**
|
|
* Get a theme logo path or view name
|
|
*
|
|
* @param string $type Logo type: 'svg_inline' or 'email_logo'
|
|
* @param string|null $default Default value if logo not configured
|
|
* @return string
|
|
*/
|
|
function theme_logo($type = 'svg_inline', $default = null)
|
|
{
|
|
return theme("logos.{$type}", $default);
|
|
}
|
|
}
|
|
|
|
if (!function_exists('theme_email_logo')) {
|
|
/**
|
|
* The theme's email logo, relative to storage/app/public. When the theme has none or its file was
|
|
* not uploaded: a text logo with the platform name (App\Services\GeneratedLogo). The tracked
|
|
* app_mail_logo(_inverted).png is the Timebank.cc logo, so only the timebank_cc theme falls back
|
|
* to it, and any theme when the text logo cannot be drawn.
|
|
*
|
|
* @param bool $inverted The light version, for dark backgrounds
|
|
* @return string
|
|
*/
|
|
function theme_email_logo(bool $inverted = false): string
|
|
{
|
|
$logo = theme_logo($inverted ? 'email_logo_inverted' : 'email_logo');
|
|
|
|
if ($logo && is_file(storage_path('app/public/' . $logo))) {
|
|
return $logo;
|
|
}
|
|
|
|
if (theme_id() !== 'timebank_cc' && ($generated = \App\Services\GeneratedLogo::path($inverted))) {
|
|
return $generated;
|
|
}
|
|
|
|
return $inverted ? 'app-images/app_mail_logo_inverted.png' : 'app-images/app_mail_logo.png';
|
|
}
|
|
}
|
|
|
|
if (!function_exists('platform_public_file')) {
|
|
/**
|
|
* A file in the platform folder's public/, as [file path, URL], when it exists and is served (the link
|
|
* public/platform made by php artisan platform:link). Null otherwise.
|
|
*
|
|
* @return array{0: string, 1: string}|null
|
|
*/
|
|
function platform_public_file(string $file): ?array
|
|
{
|
|
$file = ltrim($file, '/');
|
|
|
|
return is_file(public_path("platform/{$file}")) ? [public_path("platform/{$file}"), asset("platform/{$file}")] : null;
|
|
}
|
|
}
|
|
|
|
if (!function_exists('theme_email_logo_url')) {
|
|
/**
|
|
* URL of the email logo: platform/public/mail-logo(-inverted).png when the platform has one, else
|
|
* theme_email_logo() in storage.
|
|
*/
|
|
function theme_email_logo_url(bool $inverted = false): string
|
|
{
|
|
$platform = platform_public_file($inverted ? 'mail-logo-inverted.png' : 'mail-logo.png');
|
|
|
|
return $platform[1] ?? asset('storage/' . theme_email_logo($inverted));
|
|
}
|
|
}
|
|
|
|
if (!function_exists('theme_email_logo_file')) {
|
|
/**
|
|
* File path of the email logo (for PDFs and inline images), with the same order as theme_email_logo_url().
|
|
*/
|
|
function theme_email_logo_file(bool $inverted = false): string
|
|
{
|
|
$platform = platform_public_file($inverted ? 'mail-logo-inverted.png' : 'mail-logo.png');
|
|
|
|
return $platform[0] ?? storage_path('app/public/' . theme_email_logo($inverted));
|
|
}
|
|
}
|
|
|
|
if (!function_exists('theme_favicon')) {
|
|
/**
|
|
* URL of a favicon file (favicon.ico, favicon-32x32.png, ...). Taken from the platform folder's public/ when
|
|
* it holds the file, else from the theme's logos.favicon_dir (relative to public/), else from public/ itself.
|
|
*
|
|
* @param string $file
|
|
* @return string
|
|
*/
|
|
function theme_favicon(string $file): string
|
|
{
|
|
if ($platform = platform_public_file($file)) {
|
|
return $platform[1];
|
|
}
|
|
|
|
$dir = trim((string) theme_logo('favicon_dir'), '/');
|
|
|
|
if ($dir !== '' && is_file(public_path("{$dir}/{$file}"))) {
|
|
return asset("{$dir}/{$file}");
|
|
}
|
|
|
|
return asset($file);
|
|
}
|
|
}
|
|
|
|
if (!function_exists('theme_favicon_optional')) {
|
|
/**
|
|
* URL of an optional icon file (favicon.svg, favicon-96x96.png, site.webmanifest), or null when neither the
|
|
* platform folder, the theme's favicon_dir nor public/ has it: the layouts link it only when it exists.
|
|
*/
|
|
function theme_favicon_optional(string $file): ?string
|
|
{
|
|
if ($platform = platform_public_file($file)) {
|
|
return $platform[1];
|
|
}
|
|
|
|
$dir = trim((string) theme_logo('favicon_dir'), '/');
|
|
if ($dir !== '' && is_file(public_path("{$dir}/{$file}"))) {
|
|
return asset("{$dir}/{$file}");
|
|
}
|
|
|
|
return is_file(public_path($file)) ? asset($file) : null;
|
|
}
|
|
}
|
|
|
|
if (!function_exists('theme_og_image')) {
|
|
/**
|
|
* URL of the preview image for links shared on social media (og:image): platform/public/og-image.png, else the
|
|
* theme's logos.og_image (relative to public/), else null (no og:image tag). About 1200x630 pixels works best.
|
|
*/
|
|
function theme_og_image(): ?string
|
|
{
|
|
if ($platform = platform_public_file('og-image.png')) {
|
|
return $platform[1];
|
|
}
|
|
|
|
$image = trim((string) theme_logo('og_image'), '/');
|
|
|
|
return $image !== '' && is_file(public_path($image)) ? asset($image) : null;
|
|
}
|
|
}
|
|
|
|
if (!function_exists('theme_css_vars')) {
|
|
/**
|
|
* Generate CSS custom properties for the current theme
|
|
*
|
|
* @return string CSS custom properties as inline styles
|
|
*/
|
|
function theme_css_vars()
|
|
{
|
|
$themeConfig = theme();
|
|
$colors = $themeConfig['colors'] ?? [];
|
|
$typography = $themeConfig['typography'] ?? [];
|
|
|
|
$cssVars = [];
|
|
|
|
// Process color variables
|
|
foreach ($colors as $colorGroup => $colorValue) {
|
|
if (is_array($colorValue)) {
|
|
foreach ($colorValue as $shade => $hex) {
|
|
$rgbValue = hexToRgb($hex);
|
|
$cssVars["--color-{$colorGroup}-{$shade}"] = $rgbValue;
|
|
}
|
|
} else {
|
|
$rgbValue = hexToRgb($colorValue);
|
|
$cssVars["--color-{$colorGroup}"] = $rgbValue;
|
|
}
|
|
}
|
|
|
|
// Themes without their own secondary-button colours keep the old look (secondary / text.primary)
|
|
$cssVars['--color-button-secondary'] ??= $cssVars['--color-secondary'] ?? null;
|
|
$cssVars['--color-button-secondary-text'] ??= $cssVars['--color-text-primary'] ?? null;
|
|
$cssVars = array_filter($cssVars, fn ($value) => $value !== null);
|
|
|
|
// Process typography variables
|
|
foreach ($typography as $typographyKey => $typographyValue) {
|
|
if (is_array($typographyValue)) {
|
|
// Handle nested arrays (heading_sizes, font_sizes, font_weights)
|
|
foreach ($typographyValue as $subKey => $subValue) {
|
|
$cssVars["--{$typographyKey}-{$subKey}"] = $subValue;
|
|
}
|
|
} else {
|
|
$cssVars["--{$typographyKey}"] = $typographyValue;
|
|
}
|
|
}
|
|
|
|
// Convert to CSS string
|
|
$cssString = '';
|
|
foreach ($cssVars as $property => $value) {
|
|
$cssString .= "{$property}: {$value}; ";
|
|
}
|
|
|
|
return $cssString;
|
|
}
|
|
}
|
|
|
|
if (!function_exists('hexToRgb')) {
|
|
/**
|
|
* Convert hex color to RGB values (space-separated for CSS custom properties)
|
|
*
|
|
* @param string $hex Hex color value
|
|
* @return string RGB values separated by spaces
|
|
*/
|
|
function hexToRgb($hex)
|
|
{
|
|
$hex = ltrim($hex, '#');
|
|
|
|
if (strlen($hex) === 3) {
|
|
$hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
|
|
}
|
|
|
|
$r = hexdec(substr($hex, 0, 2));
|
|
$g = hexdec(substr($hex, 2, 2));
|
|
$b = hexdec(substr($hex, 4, 2));
|
|
|
|
return "{$r} {$g} {$b}";
|
|
}
|
|
} |