'string', 'single' => true, 'default' => '', 'show_in_rest' => true, 'sanitize_callback' => [self::class, 'sanitize'], 'auth_callback' => static fn (): bool => self::canEdit(), ]); } private static function canEdit(): bool { $taxonomy = get_taxonomy('pcwp_event_category'); return $taxonomy !== false && current_user_can($taxonomy->cap->edit_terms); } public static function sanitize(mixed $value): string { if (! is_string($value)) { return ''; } $value = trim($value); return preg_match('/^(#[a-fA-F0-9]{3}(?:[a-fA-F0-9]{3})?|var:preset\|color\|[a-zA-Z0-9_-]+)$/', $value) ? $value : ''; } public function addField(): void { echo '
'; echo ''; $this->field(''); echo '
'; } public function editField(\WP_Term $term): void { echo ''; $this->field((string) get_term_meta($term->term_id, self::META, true)); echo ''; } private function field(string $value): void { wp_nonce_field('pcwp_category_color', 'pcwp_category_color_nonce'); echo ''; echo '
'; echo '

' . esc_html__('Used by Colorful calendars. Clear to use the default pastel colors. If an event has multiple colored categories, the oldest category (lowest ID) wins. Block color overrides take precedence.', 'proeventswp') . '

'; } public function save(int $termId): void { if (! self::canEdit() || ! isset($_POST['pcwp_category_color_nonce'], $_POST[self::META]) || ! is_string($_POST['pcwp_category_color_nonce']) || ! wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['pcwp_category_color_nonce'])), 'pcwp_category_color')) { return; } $color = self::sanitize(wp_unslash($_POST[self::META])); if ($color === '') { delete_term_meta($termId, self::META); } else { update_term_meta($termId, self::META, $color); } } public function assets(): void { if (get_current_screen()?->taxonomy !== 'pcwp_event_category') { return; } wp_enqueue_style('wp-components'); wp_enqueue_script('pcwp-category-colors', PCWP_URL . 'assets/category-colors.js', ['wp-element', 'wp-components', 'wp-i18n', 'jquery'], (string) filemtime(PCWP_PATH . 'assets/category-colors.js'), true); $settings = wp_get_global_settings(); $palette = $settings['color']['palette'] ?? []; $colors = []; foreach ($palette as $origin => $items) { if ($origin === 'default' && ($settings['color']['defaultPalette'] ?? true) === false) { continue; } foreach ($items as $item) { $colors[$item['slug']] = $item; } } wp_localize_script('pcwp-category-colors', 'pcwpCategoryColors', ['colors' => array_values($colors), 'custom' => ($settings['color']['custom'] ?? true) !== false]); } /** Safe CSS for templates, resolved from WordPress-cached terms and metadata. */ public static function eventStyle(int $eventId): string { $terms = get_the_terms($eventId, 'pcwp_event_category'); if (! is_array($terms)) { return ''; } usort($terms, static fn (\WP_Term $a, \WP_Term $b): int => $a->term_id <=> $b->term_id); update_termmeta_cache(array_map(static fn (\WP_Term $term): int => $term->term_id, $terms)); foreach ($terms as $term) { $color = self::sanitize(get_term_meta($term->term_id, self::META, true)); if ($color !== '') { $color = (string) preg_replace('/^var:preset\|color\|([a-zA-Z0-9_-]+)$/', 'var(--wp--preset--color--$1)', $color); return '--pcwp-category-color:' . $color . ';'; } } return ''; } }