> */ public function controls(): array { return []; } /** * Option list for a control (GET /dynamic/options), e.g. meta keys or ACF fields. * * @return array */ public function options(string $control, string $search, Context $ctx): array { return []; } /** * @param array $settings tag settings merged with control defaults * @return string|int|float|array|null */ abstract public function render(array $settings, Context $ctx); /** @return array control defaults */ public function defaults(): array { $out = []; foreach ($this->controls() as $control) { if (isset($control['id']) && array_key_exists('default', $control)) { $out[$control['id']] = $control['default']; } } return $out; } public function group_title(): string { $titles = [ 'post' => __('Post', 'zadex'), 'archive' => __('Archive', 'zadex'), 'site' => __('Site', 'zadex'), 'media' => __('Media', 'zadex'), 'user' => __('User', 'zadex'), 'other' => __('Other', 'zadex'), 'acf' => 'ACF', 'pods' => 'Pods', 'toolset' => 'Toolset', 'meta-box' => 'Meta Box', ]; return $titles[$this->group()] ?? ucfirst($this->group()); } /** @return array editor payload (window.ZADEX.dynamicTags) */ public function to_array(): array { return [ 'name' => $this->name(), 'title' => $this->title(), 'group' => $this->group(), 'groupTitle' => $this->group_title(), 'categories' => array_values(array_intersect($this->categories(), self::CATEGORIES)), 'controls' => array_values($this->controls()), ]; } /** Filters a {value,label} list by a search string (case-insensitive, value or label). */ protected static function filter_options(array $options, string $search, int $limit = 50): array { $search = strtolower(trim($search)); $out = []; foreach ($options as $opt) { if ($search !== '' && !str_contains(strtolower($opt['value'] . ' ' . $opt['label']), $search)) { continue; } $out[] = $opt; if (count($out) >= $limit) { break; } } return $out; } /** Turns an attachment id into the media shape. */ protected static function attachment(int $id, string $size = 'full'): array { if ($id <= 0) { return ['id' => 0, 'url' => '', 'alt' => '']; } $url = function_exists('wp_get_attachment_image_url') ? (string) wp_get_attachment_image_url($id, $size) : ''; $alt = function_exists('get_post_meta') ? (string) get_post_meta($id, '_wp_attachment_image_alt', true) : ''; return ['id' => $id, 'url' => $url, 'alt' => $alt]; } /** Normalises a plugin field value (string, number, image array, list) into a tag result. */ protected static function field_value($value) { if (is_bool($value)) { return $value ? '1' : ''; } if (is_scalar($value) || $value === null) { return $value; } if (is_object($value)) { $value = (array) $value; } if (!is_array($value)) { return ''; } // Image-like field: { url, id|ID, alt }. if (isset($value['url']) && is_string($value['url'])) { return ['id' => (int) ($value['id'] ?? $value['ID'] ?? 0), 'url' => $value['url'], 'alt' => is_string($value['alt'] ?? null) ? $value['alt'] : '']; } // Meta Box image lists: first item. Plain lists: comma separated. if (array_is_list($value)) { if (isset($value[0]) && is_array($value[0]) && isset($value[0]['url'])) { return self::field_value($value[0]); } return implode(', ', array_filter(array_map(static fn($v) => is_scalar($v) ? (string) $v : '', $value), 'strlen')); } return ''; } }