'',
'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 .= '
' . $links_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 .= '- ';
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 .= '' . $links_html . '
';
}
}
$html .= ' ';
}
$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;
}
}