| | |
| | | <?php |
| | | namespace JVBase\managers; |
| | | |
| | | use JVBase\utility\Features; |
| | | use JVBase\registrar\Registrar; |
| | | use JVBase\base\Site; |
| | | |
| | | if (!defined('ABSPATH')) { |
| | | exit; |
| | |
| | | protected array $map = []; |
| | | protected const MAX_VERSIONS = 5; |
| | | |
| | | protected string $timestampKey; |
| | | protected string $iconKey; |
| | | protected string $iconHistoryKey; |
| | | protected static string $allIcons = 'icons_all_used'; |
| | | protected static string $updateIcons = 'icons_needs_update'; |
| | | protected static string $iconMap = 'icons_map'; |
| | | |
| | | |
| | | |
| | | /** |
| | | * Factory method - get or create instance for a source |
| | | */ |
| | |
| | | if (!isset(self::$instances[$source])) { |
| | | self::$instances[$source] = new self($source); |
| | | } |
| | | self::$instances[$source]->setTimestampKey(BASE.'icons_'.$source.'_timestamp'); |
| | | self::$instances[$source]->setIconKey(BASE.'icons_'.$source); |
| | | self::$instances[$source]->setIconHistoryKey(BASE.'icons_history_'.$source); |
| | | return self::$instances[$source]; |
| | | } |
| | | |
| | |
| | | private function __construct(string $source) |
| | | { |
| | | $this->source = $source; |
| | | $this->cache = Cache::for('icons_' . $source, WEEK_IN_SECONDS); |
| | | $this->style = (array_key_exists('icons', JVB_SITE) && in_array(JVB_SITE['icons'], $this->styles)) |
| | | ? JVB_SITE['icons'] |
| | | : 'regular'; |
| | | $this->cache = Cache::for('icons_' . $source); |
| | | $this->style = Site::icon(); |
| | | |
| | | $this->addMap(); |
| | | |
| | |
| | | $this->registerInstanceHooks(); |
| | | } |
| | | |
| | | protected function setIconKey(string $key):void |
| | | { |
| | | $this->iconKey = $key; |
| | | } |
| | | protected function setTimestampKey(string $key):void |
| | | { |
| | | $this->timestampKey = $key; |
| | | } |
| | | protected function setIconHistoryKey(string $key):void |
| | | { |
| | | $this->iconHistoryKey = $key; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | |
| | | */ |
| | | protected function loadStoredIcons(): void |
| | | { |
| | | $allIcons = get_option(BASE.'usedIcons', []); |
| | | $allIcons = get_option(BASE.self::$allIcons, []); |
| | | $storedIcons = $allIcons[$this->source] ?? []; |
| | | |
| | | // Merge stored icons with any existing icons (like custom icons) |
| | |
| | | 'apple-logo', |
| | | 'check-circle', |
| | | 'close-circle', |
| | | 'faders-horizontal', |
| | | 'cloud-slash', |
| | | 'exclamation-mark', |
| | | 'cloud-arrow-down', |
| | |
| | | // ] |
| | | ]; |
| | | |
| | | |
| | | // Add icons from content/taxonomy/user configs (like old behavior) |
| | | $configIcons = $this->getIconsFromConfigs(); |
| | | if (!empty($configIcons)) { |
| | | foreach ($configIcons as $source => $icons) { |
| | | if (!isset($defaults[$source])) { |
| | | $defaults[$source] = []; |
| | | } |
| | | $defaults[$source] = array_merge($defaults[$source], $icons); |
| | | } |
| | | $defaults['icons'] = array_merge($defaults['icons'], $configIcons); |
| | | } |
| | | |
| | | // Allow filtering per source (extensibility) |
| | |
| | | if (isset($allIcons[$this->source])) { |
| | | $icons = array_merge($icons, $allIcons[$this->source]); |
| | | } |
| | | |
| | | if (!empty($icons)) { |
| | | $this->include($icons); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Get icons from JVB_CONTENT, JVB_TAXONOMY, JVB_USER configs |
| | | * Get icons from Registrar instances |
| | | * |
| | | */ |
| | | protected function getIconsFromConfigs(): array |
| | | { |
| | | $icons = []; |
| | | $check = [JVB_CONTENT, JVB_TAXONOMY, JVB_USER]; |
| | | $registered = Registrar::getRegistered(); |
| | | |
| | | foreach ($check as $constant) { |
| | | foreach ($constant as $key => $value) { |
| | | if (isset($value['icon'])) { |
| | | // Determine source based on context (you could add 'icon_source' to configs) |
| | | $source = $value['icon_source'] ?? 'icons'; |
| | | |
| | | if (!isset($icons[$source])) { |
| | | $icons[$source] = []; |
| | | } |
| | | $icons[$source][] = $value['icon']; |
| | | } |
| | | } |
| | | foreach ($registered as $type) { |
| | | $registrar = Registrar::getInstance($type); |
| | | $icons[] = $registrar->getIcon(); |
| | | } |
| | | |
| | | return $icons; |
| | | |
| | | return array_unique(array_filter($icons)); |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | protected function saveIcons(): void |
| | | { |
| | | $allIcons = []; |
| | | foreach (self::$instances as $source => $instance) { |
| | | $allIcons[$source] = $instance->icons; |
| | | } |
| | | $allIcons = array_map(function ($instance) { |
| | | return $instance->icons; |
| | | }, self::$instances); |
| | | |
| | | update_option(BASE.'usedIcons', $allIcons); |
| | | update_option(BASE.self::$allIcons, $allIcons); |
| | | |
| | | // Track WHICH source needs updating |
| | | $needsUpdate = get_option(BASE.'icons_needs_update', []); |
| | | $needsUpdate = get_option(BASE.self::$updateIcons, []); |
| | | if (!is_array($needsUpdate)) { |
| | | $needsUpdate = []; |
| | | } |
| | | $needsUpdate[$this->source] = true; |
| | | update_option(BASE.'icons_needs_update', $needsUpdate); |
| | | update_option(BASE.self::$updateIcons, $needsUpdate); |
| | | } |
| | | |
| | | /** |
| | |
| | | |
| | | protected function addMap(): void |
| | | { |
| | | $map = get_option(BASE.'iconMap'); |
| | | $map = get_option(BASE.self::$iconMap); |
| | | if (!$map) { |
| | | $map = [ |
| | | 'seo' => 'robot' |
| | | ]; |
| | | if (Features::forSite()->has('referrals')) { |
| | | if (Site::has('referrals')) { |
| | | $map['referrals'] = 'hand-heart'; |
| | | } |
| | | if (Features::forSite()->has('dashboard')) { |
| | | if (Site::has('dashboard')) { |
| | | $map['dash'] = 'door'; |
| | | } |
| | | if (Features::forSite()->has('magicLink')) { |
| | | if (Site::has('magic_link')) { |
| | | $map['magicLink'] = 'magic-wand'; |
| | | } |
| | | if (Features::hasAnyIntegration()) { |
| | | if (Site::hasAnyIntegration()) { |
| | | $map['integrations'] = 'plugs-connected'; |
| | | } |
| | | update_option(BASE.'iconMap', $map); |
| | | update_option(BASE.self::$iconMap, $map); |
| | | } |
| | | |
| | | $this->map = apply_filters('jvbMapIcons', $map); |
| | |
| | | protected function registerGlobalHooks(): void |
| | | { |
| | | add_action('wp_loaded', [self::class, 'checkCSS']); |
| | | add_action('shutdown', [self::class, 'checkCSS']); |
| | | } |
| | | |
| | | /** |
| | |
| | | |
| | | public function enqueueIconStyles():void |
| | | { |
| | | wp_enqueue_style('jvb-icons-'.$this->source); |
| | | // if (file_exists(JVB_CHILD_URL . "assets/css/{$this->source}.css")){ |
| | | wp_enqueue_style('jvb-icons-'.$this->source); |
| | | // } |
| | | |
| | | } |
| | | |
| | | public static function checkCSS(): void |
| | | { |
| | | $needsUpdate = get_option(BASE.'icons_needs_update', []); |
| | | $needsUpdate = get_option(BASE.self::$updateIcons, []); |
| | | $needsUpdate = array_filter($needsUpdate, function ($item) { |
| | | return $item === true; |
| | | }); |
| | | if (!empty($needsUpdate)) { |
| | | error_log('Regenerating CSS for sources: ' . implode(', ', array_keys($needsUpdate))); |
| | | delete_option(BASE.'icons_needs_update'); |
| | | delete_option(BASE.self::$updateIcons); |
| | | self::regenerateAllCSS($needsUpdate); |
| | | } |
| | | } |
| | |
| | | } |
| | | |
| | | // Load all icons from database option |
| | | $allIcons = get_option(BASE.'usedIcons', []); |
| | | $allIcons = get_option(BASE.self::$allIcons, []); |
| | | |
| | | // If no specific sources provided, regenerate all |
| | | if (empty($sourcesToUpdate)) { |
| | |
| | | $instance->archiveCurrentVersion($css); |
| | | |
| | | if (file_put_contents($css_path, $css) !== false) { |
| | | Cache::touch('icons_' . $source); |
| | | Cache::for('icons_'.$source)->flush(); |
| | | error_log("[IconsManager] Updated {$source}.css"); |
| | | } else { |
| | | error_log("[IconsManager] Could not write {$source}.css"); |
| | |
| | | $instance->archiveCurrentVersion($css); |
| | | |
| | | if (file_put_contents($css_path, $css) !== false) { |
| | | Cache::touch('icons_' . $source); |
| | | Cache::for('icons_'.$source)->flush(); |
| | | error_log("[IconsManager] Updated {$source}.css"); |
| | | } else { |
| | | error_log("[IconsManager] Could not write {$source}.css"); |
| | |
| | | */ |
| | | public function get(string $name, array $options = []): string |
| | | { |
| | | if ($name === '') { |
| | | if (empty($name)) { |
| | | //No icon requested |
| | | return ''; |
| | | } |
| | |
| | | } |
| | | |
| | | // Track usage - only if not already tracked |
| | | if (!isset($this->icons[$style])) { |
| | | if (!array_key_exists($style, $this->icons)) { |
| | | $this->icons[$style] = []; |
| | | } |
| | | |
| | |
| | | /** |
| | | * Get raw SVG content for CSS mask-image |
| | | */ |
| | | protected function getRawSvg(string $name, ?string $style = null): ?string |
| | | public function getRawSvg(string $name, ?string $style = null): ?string |
| | | { |
| | | if (!$style) { |
| | | $style = $this->style; |
| | |
| | | |
| | | public function registerStyle(): void |
| | | { |
| | | $timestamp = Cache::lastModified('icons_' . $this->source); |
| | | $timestamp = get_option($this->timestampKey); |
| | | $handle = 'jvb-icons-' . $this->source; |
| | | |
| | | wp_register_style( |
| | |
| | | foreach ($names as $icon) { |
| | | $svg = $this->getEncodedSVG($icon, $style); |
| | | if ($svg !== '') { |
| | | if ($icon === 'caret-down') { |
| | | $css .= 'details summary::after,'; |
| | | } elseif ($icon === 'faders-horizontal') { |
| | | $css .= 'details.all-filters summary::after,'; |
| | | } elseif ($icon === 'link') { |
| | | $css .= 'input[type=url],'; |
| | | } elseif ($icon === apply_filters('jvbSeparatorLogo', 'logo')) { |
| | | $css .= 'hr.logo::before,'; |
| | | } |
| | | $css .= ".icon-{$icon}{$styleClass}{"; |
| | | $css .= "--icon:url('data:image/svg+xml;base64,{$svg}');"; |
| | | $css .= "}"; |
| | | } |
| | | } |
| | | } |
| | | |
| | | update_option($this->timestampKey, time()); |
| | | return $this->minifyCss($css); |
| | | } |
| | | |
| | |
| | | if (!$style) { |
| | | $style = $this->style; |
| | | } |
| | | |
| | | $icon = $this->map[$icon] ?? $icon; |
| | | |
| | | // Validate icon exists |
| | | if (!$this->iconExists($icon, $style)) { |
| | | error_log('[IconsManager] Icon not found: ' . $icon); |
| | | return ''; |
| | | } |
| | | $svg = $this->getEncodedSVG($icon, $style); |
| | | if ($svg !== '') { |
| | | return "data:image/svg+xml;base64,{$svg}"; |
| | |
| | | */ |
| | | public function clearIconCache(): void |
| | | { |
| | | delete_option(BASE . 'icon_usage_list'); // Legacy |
| | | delete_option(BASE.'usedIcons'); |
| | | delete_option(BASE.'iconMap'); |
| | | delete_option(self::$allIcons); |
| | | delete_option(self::$iconMap); |
| | | |
| | | // Clear cache for all sources |
| | | foreach (self::$instances as $source => $instance) { |
| | |
| | | $history = array_slice($history, 0, self::MAX_VERSIONS); |
| | | } |
| | | |
| | | update_option(BASE.'icon_css_history_' . $this->source, $history); |
| | | update_option($this->iconHistoryKey, $history); |
| | | } |
| | | |
| | | public function getVersionHistory(): array |
| | | { |
| | | return get_option(BASE.'icon_css_history_' . $this->source, []); |
| | | return get_option($this->iconHistoryKey, []); |
| | | } |
| | | |
| | | public function restoreVersion(int $timestamp): bool |
| | |
| | | if (file_put_contents($css_path, $entry['css']) !== false) { |
| | | $this->icons = $entry['iconList']; |
| | | $this->saveIcons(); |
| | | Cache::touch('icons_' . $this->source); |
| | | Cache::for('icons_'.$this->source)->flush(); |
| | | return true; |
| | | } |
| | | |
| | |
| | | public function forceRefresh(): void |
| | | { |
| | | $this->clearIconCache(); |
| | | $needsUpdate = get_option(BASE.'icons_needs_update', []); |
| | | $needsUpdate = get_option(BASE.self::$updateIcons, []); |
| | | if (!is_array($needsUpdate)) { |
| | | $needsUpdate = []; |
| | | } |
| | | $needsUpdate[$this->source] = true; |
| | | update_option(BASE.'icons_needs_update', $needsUpdate); |
| | | Cache::touch('icons_' . $this->source); |
| | | update_option(BASE.self::$updateIcons, $needsUpdate); |
| | | |
| | | Cache::for('icons_'.$this->source)->flush(); |
| | | } |
| | | |
| | | public function mergeVersions(array $timestamps): bool |
| | |
| | | return false; |
| | | } |
| | | |
| | | $history = get_option(BASE.'icon_css_history_' . $this->source, []); |
| | | $history = get_option($this->iconHistoryKey, []); |
| | | $merged_icons = []; |
| | | |
| | | // Collect icons from selected versions |