- BrandingAdminController: GET/PUT /api/my-branding (Farben, Tagline), Logo-Upload/-Löschen, öffentliche Logo-Auslieferung /api/branding/logo/... - BrandingService liefert logoUrl aus S3-logoKey (Firma → Reseller → Default) - BrandingView (Reseller-Topnav + Firmen-Sidebar): Farbwähler, Slogan, Logo-Upload mit Live-Vorschau, Anzeige der eigenen Adresse Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
100 lines
3.3 KiB
PHP
100 lines
3.3 KiB
PHP
<?php
|
|
|
|
namespace App\Service;
|
|
|
|
use App\Entity\Company;
|
|
use App\Entity\Reseller;
|
|
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
|
|
|
/**
|
|
* Baut aus einem aufgelösten Tenant das Branding (Name, Farben, Logo) für die SPA.
|
|
* Fallback-Kette: Firma → Reseller → Plattform-Standard. So erbt eine Firma ohne
|
|
* eigenes Branding automatisch das ihres Resellers.
|
|
*/
|
|
final class BrandingService
|
|
{
|
|
public const SCOPE_RESELLER = 'reseller';
|
|
public const SCOPE_COMPANY = 'company';
|
|
|
|
/** Plattform-Standard (vcard4reseller-Orange). */
|
|
private const COLOR_DEFAULTS = [
|
|
'primaryColor' => '#f58220',
|
|
'primaryColorDark' => '#d96500',
|
|
'primaryColorSoft' => '#fff2e7',
|
|
'tagline' => null,
|
|
];
|
|
|
|
public function __construct(private readonly UrlGeneratorInterface $urls)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* @return array{level:string,name:string,reseller:?string,customDomain:bool,branding:array<string,mixed>}
|
|
*/
|
|
public function forTenant(ResolvedTenant $tenant): array
|
|
{
|
|
$branding = self::COLOR_DEFAULTS;
|
|
foreach ($this->configChain($tenant) as $config) {
|
|
foreach (self::COLOR_DEFAULTS as $key => $_) {
|
|
if (isset($config[$key]) && '' !== $config[$key]) {
|
|
$branding[$key] = $config[$key];
|
|
}
|
|
}
|
|
}
|
|
$branding['logoUrl'] = $this->logoUrl($tenant);
|
|
|
|
return [
|
|
'level' => $tenant->kind,
|
|
'name' => $this->name($tenant),
|
|
'reseller' => $tenant->reseller?->getName(),
|
|
'customDomain' => $tenant->customDomain,
|
|
'branding' => $branding,
|
|
];
|
|
}
|
|
|
|
/** Öffentliche Logo-URL eines Tenants (am spezifischsten zuerst), oder null. */
|
|
public function logoUrl(ResolvedTenant $tenant): ?string
|
|
{
|
|
if (null !== $tenant->company && '' !== (string) ($tenant->company->getBrandingConfig()['logoKey'] ?? '')) {
|
|
return $this->logoRoute(self::SCOPE_COMPANY, (string) $tenant->company->getId(), $tenant->company->getBrandingConfig()['logoKey']);
|
|
}
|
|
if (null !== $tenant->reseller && '' !== (string) ($tenant->reseller->getBrandingConfig()['logoKey'] ?? '')) {
|
|
return $this->logoRoute(self::SCOPE_RESELLER, (string) $tenant->reseller->getId(), $tenant->reseller->getBrandingConfig()['logoKey']);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/** @return array<int, array<string,mixed>> */
|
|
private function configChain(ResolvedTenant $tenant): array
|
|
{
|
|
$chain = [];
|
|
if (null !== $tenant->reseller) {
|
|
$chain[] = $tenant->reseller->getBrandingConfig();
|
|
}
|
|
if (null !== $tenant->company) {
|
|
$chain[] = $tenant->company->getBrandingConfig();
|
|
}
|
|
|
|
return $chain;
|
|
}
|
|
|
|
private function logoRoute(string $scope, string $id, mixed $logoKey): string
|
|
{
|
|
return $this->urls->generate('branding_logo', ['scope' => $scope, 'id' => $id])
|
|
.'?v='.substr(sha1((string) $logoKey), 0, 8);
|
|
}
|
|
|
|
private function name(ResolvedTenant $tenant): string
|
|
{
|
|
if ($tenant->isCompany() && $tenant->company instanceof Company) {
|
|
return $tenant->company->getName();
|
|
}
|
|
if ($tenant->isReseller() && $tenant->reseller instanceof Reseller) {
|
|
return $tenant->reseller->getName();
|
|
}
|
|
|
|
return 'vcard4reseller';
|
|
}
|
|
}
|