'', 'post_type' => '', 'taxonomy' => '', 'request' => array(), ), $context ); $items = apply_filters( 'ait_aiaa_third_party_information', array(), $context ); if ( ! is_array( $items ) ) { return array(); } $out = array(); foreach ( $items as $raw ) { $item = $this->normalize_item( $raw ); if ( empty( $item ) ) { continue; } if ( ! $this->matches_context( $item, $context ) ) { continue; } if ( ! $this->passes_capability( $item ) ) { continue; } $out[] = $item; } usort( $out, function( $a, $b ) { $pa = isset( $a['priority'] ) ? (int) $a['priority'] : 10; $pb = isset( $b['priority'] ) ? (int) $b['priority'] : 10; if ( $pa === $pb ) { return 0; } return ( $pa < $pb ) ? -1 : 1; } ); return $out; } public function render_items_html( $items ) { if ( ! is_array( $items ) || empty( $items ) ) { return '
' . esc_html__( 'No third-party help available for this screen.', 'ait-ai-admin-assistance' ) . '
'; } $html = ''; foreach ( $items as $item ) { $title = isset( $item['title'] ) ? $item['title'] : ''; $desc = isset( $item['description'] ) ? $item['description'] : ''; $notes = isset( $item['notes'] ) ? $item['notes'] : ''; $icon = isset( $item['icon'] ) ? $item['icon'] : ''; $source = ( isset( $item['source'] ) && is_array( $item['source'] ) ) ? $item['source'] : array(); $source_name = isset( $source['name'] ) ? $source['name'] : ''; $source_type = isset( $source['type'] ) ? $source['type'] : ''; $label = $source_name; if ( $label !== '' && $source_type !== '' ) { $label .= ' (' . $source_type . ')'; } $icon_html = ''; if ( is_string( $icon ) && $icon !== '' ) { $icon = trim( $icon ); if ( 0 === strpos( $icon, 'dashicons-' ) ) { $icon_html = ''; } elseif ( preg_match( '#^https?://#i', $icon ) ) { $icon_html = ''; } } $html .= '
'; $html .= '
'; $html .= '
' . $icon_html . '
' . esc_html( $title ) . '
'; if ( $label !== '' ) { $html .= '
' . esc_html( $label ) . '
'; } $html .= '
'; // This wrapper is what gets saved into bookmarks (answer HTML). $html .= '
'; if ( $desc !== '' ) { // HTML allowed (sanitized). $html .= '
' . wp_kses_post( wpautop( $desc ) ) . '
'; } if ( ! empty( $item['help_links'] ) && is_array( $item['help_links'] ) ) { $links_html = $this->render_help_links_html( $item['help_links'] ); if ( '' !== $links_html ) { $html .= ' '; } } if ( ! empty( $item['steps'] ) && is_array( $item['steps'] ) ) { $html .= '
    '; foreach ( $item['steps'] as $step ) { $s_title = isset( $step['title'] ) ? $step['title'] : ''; $s_body = isset( $step['content'] ) ? $step['content'] : ''; $html .= '
  1. '; if ( $s_title !== '' ) { $html .= '
    ' . esc_html( $s_title ) . '
    '; } if ( $s_body !== '' ) { // HTML allowed (sanitized). $html .= '
    ' . wp_kses_post( wpautop( $s_body ) ) . '
    '; } if ( ! empty( $step['help_links'] ) && is_array( $step['help_links'] ) ) { $links_html = $this->render_help_links_html( $step['help_links'], 1 ); if ( '' !== $links_html ) { $html .= ''; } } $html .= '
  2. '; } $html .= '
'; } if ( ! empty( $notes ) ) { $html .= '
' . wp_kses_post( wpautop( $notes ) ) . '
'; } $html .= '
'; // bookmark wrapper $html .= '
'; $html .= ' '; $html .= '
'; $html .= '
'; } return $html; } protected function normalize_item( $raw ) { if ( ! is_array( $raw ) ) { return array(); } $title = isset( $raw['title'] ) ? sanitize_text_field( $raw['title'] ) : ''; if ( '' === $title ) { return array(); } $id = isset( $raw['id'] ) ? sanitize_key( $raw['id'] ) : ''; if ( '' === $id ) { $id = 'tp_' . substr( md5( wp_json_encode( $raw ) ), 0, 12 ); } $priority = isset( $raw['priority'] ) ? (int) $raw['priority'] : 10; $match = isset( $raw['match'] ) ? sanitize_key( $raw['match'] ) : 'any'; if ( 'any' !== $match && 'all' !== $match ) { $match = 'any'; } $capability = isset( $raw['capability'] ) ? $raw['capability'] : ''; $icon = isset( $raw['icon'] ) && is_string( $raw['icon'] ) ? trim( $raw['icon'] ) : ''; $item = array( 'id' => $id, 'title' => $title, 'description' => isset( $raw['description'] ) ? $raw['description'] : '', 'priority' => $priority, 'match' => $match, 'screen_ids' => ( isset( $raw['screen_ids'] ) && is_array( $raw['screen_ids'] ) ) ? array_map( 'sanitize_text_field', $raw['screen_ids'] ) : array(), 'post_types' => ( isset( $raw['post_types'] ) && is_array( $raw['post_types'] ) ) ? array_map( 'sanitize_key', $raw['post_types'] ) : array(), 'taxonomies' => ( isset( $raw['taxonomies'] ) && is_array( $raw['taxonomies'] ) ) ? array_map( 'sanitize_key', $raw['taxonomies'] ) : array(), 'target_callback' => ( isset( $raw['target_callback'] ) && is_callable( $raw['target_callback'] ) ) ? $raw['target_callback'] : null, 'capability' => $capability, 'icon' => $icon, 'source' => ( isset( $raw['source'] ) && is_array( $raw['source'] ) ) ? array( 'type' => isset( $raw['source']['type'] ) ? sanitize_key( $raw['source']['type'] ) : '', 'name' => isset( $raw['source']['name'] ) ? sanitize_text_field( $raw['source']['name'] ) : '', 'slug' => isset( $raw['source']['slug'] ) ? sanitize_key( $raw['source']['slug'] ) : '', ) : array(), 'help_links' => array(), 'steps' => array(), 'notes' => isset( $raw['notes'] ) ? $raw['notes'] : '', ); // help_links optional (recursive groups + links) if ( isset( $raw['help_links'] ) && is_array( $raw['help_links'] ) ) { $item['help_links'] = $this->normalize_help_links( $raw['help_links'] ); } // steps optional (HTML content allowed) if ( isset( $raw['steps'] ) && is_array( $raw['steps'] ) ) { foreach ( $raw['steps'] as $s ) { if ( ! is_array( $s ) ) { continue; } $st = isset( $s['title'] ) ? sanitize_text_field( $s['title'] ) : ''; $sc = isset( $s['content'] ) ? $s['content'] : ''; $step = array( 'title' => $st, 'content' => $sc, 'help_links' => array(), ); if ( isset( $s['help_links'] ) && is_array( $s['help_links'] ) ) { $step['help_links'] = $this->normalize_help_links( $s['help_links'] ); } if ( '' !== $step['title'] || '' !== $step['content'] || ! empty( $step['help_links'] ) ) { $item['steps'][] = $step; } } } // Require at least one target OR a callback if ( empty( $item['target_callback'] ) && empty( $item['screen_ids'] ) && empty( $item['post_types'] ) && empty( $item['taxonomies'] ) ) { return array(); } return $item; } protected function render_help_links_html( $help_links, $base_depth = 0 ) { if ( ! is_array( $help_links ) || empty( $help_links ) ) { return ''; } $parts = $this->render_help_links_level( $help_links, (int) $base_depth ); return is_array( $parts ) ? implode( '', $parts ) : ''; } protected function render_help_links_level( $node, $depth ) { $out = array(); if ( ! is_array( $node ) || empty( $node ) ) { return $out; } $has_prev_section = false; foreach ( $node as $k => $v ) { // Group with title (string key). if ( is_string( $k ) && ! is_numeric( $k ) ) { $title = sanitize_text_field( $k ); if ( '' === $title ) { continue; } if ( $has_prev_section ) { $out[] = '
'; } $out[] = '
' . esc_html( $title ) . '
'; $children = $this->render_help_links_level( $v, $depth + 1 ); if ( ! empty( $children ) ) { $out[] = '
' . implode( '', $children ) . '
'; } $has_prev_section = true; continue; } // Link node if ( $this->is_help_link_node( $v ) ) { $title = isset( $v['title'] ) ? (string) $v['title'] : ''; $url = isset( $v['url'] ) ? (string) $v['url'] : ''; if ( '' === $title || '' === $url ) { continue; } $out[] = ''; continue; } // Anonymous subgroup at same depth (supports arrays of arrays) if ( is_array( $v ) ) { $children = $this->render_help_links_level( $v, $depth + 1 ); if ( empty( $children ) ) { continue; } if ( $has_prev_section ) { $out[] = '
'; } $out[] = '
' . implode( '', $children ) . '
'; $has_prev_section = true; } } return $out; } protected function normalize_help_links( $node, $depth = 0, &$count = 0 ) { $depth = (int) $depth; if ( $depth > 10 ) { return array(); } // safety cap if ( ! is_array( $node ) ) { return array(); } $out = array(); foreach ( $node as $k => $v ) { // Cap total rendered links. if ( $count >= 80 ) { break; } // Group key if ( is_string( $k ) && ! is_numeric( $k ) ) { $key = sanitize_text_field( $k ); if ( '' === $key || ! is_array( $v ) ) { continue; } $child = $this->normalize_help_links( $v, $depth + 1, $count ); if ( ! empty( $child ) ) { $out[ $key ] = $child; } continue; } // Direct link if ( is_array( $v ) && $this->is_help_link_node( $v ) ) { $title = isset( $v['title'] ) ? sanitize_text_field( $v['title'] ) : ''; $url = isset( $v['url'] ) ? esc_url_raw( $v['url'] ) : ''; if ( '' === $title || '' === $url ) { continue; } $out[] = array( 'title' => $title, 'url' => $url ); $count++; continue; } // Anonymous subgroup if ( is_array( $v ) ) { $child = $this->normalize_help_links( $v, $depth + 1, $count ); if ( ! empty( $child ) ) { $out[] = $child; } } } return $out; } protected function is_help_link_node( $v ) { return is_array( $v ) && isset( $v['title'], $v['url'] ) && is_string( $v['title'] ) && is_string( $v['url'] ); } protected function matches_context( $item, $context ) { if ( isset( $item['target_callback'] ) && is_callable( $item['target_callback'] ) ) { return (bool) call_user_func( $item['target_callback'], $context, $item ); } $screen_id = isset( $context['screen_id'] ) ? (string) $context['screen_id'] : ''; $post_type = isset( $context['post_type'] ) ? (string) $context['post_type'] : ''; $taxonomy = isset( $context['taxonomy'] ) ? (string) $context['taxonomy'] : ''; $checks = array(); if ( ! empty( $item['screen_ids'] ) ) { $checks[] = in_array( $screen_id, $item['screen_ids'], true ); } if ( ! empty( $item['post_types'] ) ) { $checks[] = in_array( $post_type, $item['post_types'], true ); } if ( ! empty( $item['taxonomies'] ) ) { $checks[] = in_array( $taxonomy, $item['taxonomies'], true ); } if ( empty( $checks ) ) { return false; } if ( 'all' === $item['match'] ) { foreach ( $checks as $ok ) { if ( ! $ok ) { return false; } } return true; } foreach ( $checks as $ok ) { if ( $ok ) { return true; } } return false; } protected function passes_capability( $item ) { if ( empty( $item['capability'] ) ) { return true; } $cap = $item['capability']; if ( is_string( $cap ) ) { $cap = trim( $cap ); if ( '' === $cap ) { return true; } return current_user_can( $cap ); } if ( is_array( $cap ) ) { foreach ( $cap as $c ) { $c = is_string( $c ) ? trim( $c ) : ''; if ( '' === $c ) { continue; } if ( current_user_can( $c ) ) { return true; } } return false; } return true; } }