admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'aije_insert_link' ) ) ) . ';', 'before' );
}
}
/* ===== Admin-Menü ===== */
public function tools_menu() {
add_management_page(
esc_html__( 'WP Rankify', 'ai-json-extractor-auto-embed-lite' ),
esc_html__( 'WP Rankify', 'ai-json-extractor-auto-embed-lite' ),
'manage_options',
'aije-lite',
array( $this, 'render_tools' )
);
}
public function render_tools() {
if ( ! current_user_can( 'manage_options' ) ) { return; }
$licensed = self::license_is_valid();
$tabs = array(
'dashboard' => esc_html__( 'Dashboard', 'ai-json-extractor-auto-embed-lite' ),
'settings' => esc_html__( 'Einstellungen', 'ai-json-extractor-auto-embed-lite' ),
'tracking' => esc_html__( 'Tracking & Analytics', 'ai-json-extractor-auto-embed-lite' ),
'gsc' => esc_html__( 'Google Suche', 'ai-json-extractor-auto-embed-lite' ),
'llms' => esc_html__( 'llms.txt', 'ai-json-extractor-auto-embed-lite' ),
'license' => esc_html__( 'Lizenz', 'ai-json-extractor-auto-embed-lite' ),
);
$tab = isset( $_GET['tab'] ) ? sanitize_key( $_GET['tab'] ) : 'dashboard';
echo '
';
echo '
' . esc_html__( 'WP Rankify', 'ai-json-extractor-auto-embed-lite' ) . ' (AI Auto Embed) ';
echo '
';
foreach ( $tabs as $k => $label ) {
$class = ( $tab === $k ) ? ' nav-tab nav-tab-active' : ' nav-tab';
$url = add_query_arg( array( 'page' => 'aije-lite', 'tab' => $k ), admin_url( 'tools.php' ) );
echo '' . esc_html( $label ) . ' ';
}
echo ' ';
echo '
⭐ ' . esc_html__( 'WP Rankify bewerten', 'ai-json-extractor-auto-embed-lite' ) . '
';
if ( $licensed ) {
echo '
' . esc_html__( 'Lizenz aktiv.', 'ai-json-extractor-auto-embed-lite' ) . '
';
}
$admin_dir = plugin_dir_path( AIJE_PLUGIN_FILE ) . 'admin/';
switch ( $tab ) {
case 'dashboard': require $admin_dir . 'tab-dashboard.php'; break;
case 'gsc': require $admin_dir . 'tab-gsc.php'; break;
case 'llms': require $admin_dir . 'tab-llms.php'; break;
case 'license': require $admin_dir . 'tab-license.php'; break;
case 'tracking': require $admin_dir . 'tab-tracking.php'; break;
case 'settings':
default: require $admin_dir . 'tab-settings.php'; break;
}
echo '
';
}
/* ===== Metaboxen ===== */
public function add_score_metabox() {
foreach ( $this->get_opts()['enabled_post_types'] as $t ) {
add_meta_box( 'aije_score', esc_html__( 'WP Rankify – Readiness Score', 'ai-json-extractor-auto-embed-lite' ), array( $this, 'render_score_metabox' ), $t, 'side', 'high' );
}
}
public function render_score_metabox( $post ) {
$analysis = $this->calculate_ai_analysis( $post );
$score = $analysis['score'];
$cats = $analysis['categories'];
wp_nonce_field( 'aije_ai_nonce', 'aije_ai_nonce_field' );
$color = $score >= 75 ? '#27ae60' : ( $score >= 50 ? '#e67e22' : '#e74c3c' );
$label = $score >= 75 ? __( 'Gut', 'ai-json-extractor-auto-embed-lite' ) : ( $score >= 50 ? __( 'Optimierbar', 'ai-json-extractor-auto-embed-lite' ) : __( 'Kritisch', 'ai-json-extractor-auto-embed-lite' ) );
echo '';
echo '
' . esc_html( $score ) . '
';
echo '
' . esc_html( $label ) . '
';
echo '
';
echo '' . esc_html__( 'Kategorien', 'ai-json-extractor-auto-embed-lite' ) . '
';
$bar_colors = array( 'content' => '#3498db', 'eeat' => '#9b59b6', 'technical' => '#1abc9c', 'linking' => '#e67e22' );
$link_stats = $this->get_link_stats( $post->ID );
foreach ( $cats as $key => $cat ) {
$bar_color = $bar_colors[ $key ] ?? '#999';
echo '';
echo '
';
echo '' . esc_html( $cat['label'] ) . ' (' . esc_html( $cat['weight'] ) . '%) ';
if ( 'linking' === $key ) {
$tip = sprintf(
/* translators: 1: aktuelle Links, 2: empfohlene Links, 3: Wortanzahl */
__( 'Faustregel: ca. 1 interner Link pro 200–250 Wörter. Bei %3$d Wörtern werden ~%2$d empfohlen, aktuell sind es %1$d.', 'ai-json-extractor-auto-embed-lite' ),
$link_stats['links'], $link_stats['recommended'], $link_stats['word_count']
);
echo ' — 🔗 ' . esc_html( $link_stats['links'] ) . '/' . esc_html( $link_stats['recommended'] );
echo $link_stats['remaining'] > 0
? ' · ' . esc_html( sprintf( _n( 'noch %d sinnvoll', 'noch %d sinnvoll', $link_stats['remaining'], 'ai-json-extractor-auto-embed-lite' ), $link_stats['remaining'] ) )
: ' · ' . esc_html__( 'Empfehlung erreicht', 'ai-json-extractor-auto-embed-lite' );
echo ' ';
}
echo ' ';
echo '' . esc_html( $cat['score'] ) . ' ';
echo '
';
echo '
';
echo '
';
}
if ( ! empty( $analysis['issues'] ) ) {
echo ' ';
echo '' . esc_html__( 'Optimierungspotenzial', 'ai-json-extractor-auto-embed-lite' ) . '
';
$severity_styles = array( 'high' => 'background:#fdecea;color:#c0392b;', 'medium' => 'background:#fef9e7;color:#b7770d;', 'low' => 'background:#eafaf1;color:#1e8449;' );
$severity_labels = array( 'high' => __( 'Hoch', 'ai-json-extractor-auto-embed-lite' ), 'medium' => __( 'Mittel', 'ai-json-extractor-auto-embed-lite' ), 'low' => __( 'Gering', 'ai-json-extractor-auto-embed-lite' ) );
$cat_icons = array( 'content' => '✍', 'eeat' => '🏅', 'technical' => '⚙', 'linking' => '🔗' );
$all_advice = array();
foreach ( $analysis['issues'] as $idx => $issue ) { $all_advice[ $idx ] = $this->build_issue_advice( $issue, $post, $analysis ); }
$total_issues = count( $analysis['issues'] );
$scrollable = $total_issues > 3;
$max_h = $scrollable ? 'max-height:340px;overflow-y:auto;padding-right:4px;' : '';
echo '';
foreach ( $analysis['issues'] as $idx => $issue ) {
$sev_style = $severity_styles[ $issue['severity'] ] ?? '';
$sev_label = $severity_labels[ $issue['severity'] ] ?? '';
$icon = $cat_icons[ $issue['category'] ] ?? '•';
$border_c = $bar_colors[ $issue['category'] ] ?? '#ccc';
echo '
';
echo '
';
echo '' . esc_html( $icon ) . ' ' . esc_html( $issue['msg'] ) . ' ';
echo '' . esc_html( $sev_label ) . ' ';
echo '
';
echo '
💡 ' . esc_html__( 'Wie verbessere ich das konkret?', 'ai-json-extractor-auto-embed-lite' ) . ' ';
echo '
';
}
echo '
';
if ( $scrollable ) { echo '' . sprintf( esc_html__( '%d Hinweise – scrollen ↕', 'ai-json-extractor-auto-embed-lite' ), $total_issues ) . '
'; }
echo '';
}
$this->render_inbound_links_box( $this->get_inbound_link_report( $post ), $post->ID );
$this->render_meta_generator_box( $post );
$this->render_topic_analysis_box( $post );
// Advice-Overlay + JS aus separater Datei
require plugin_dir_path( AIJE_PLUGIN_FILE ) . 'admin/metabox-overlay.php';
}
/**
* "Wer verlinkt hierher?" — zeigt, welche anderen Beiträge bereits auf
* diesen hier verlinken, und welche thematisch passenden Beiträge das
* noch nicht tun (Lücken für gezieltes internes Verlinken).
*/
private function render_inbound_links_box( $inbound, $post_id ) {
$D = 'ai-json-extractor-auto-embed-lite';
echo ' ';
echo '🔗 ' . esc_html__( 'Wer verlinkt hierher?', $D ) . '
';
// Wie oft verlinkt DIESER Beitrag selbst schon auf andere Seiten
// (ausgehend) — unabhängig von der Frage, wer auf ihn zurückverlinkt.
// Plus Empfehlung nach Faustregel (~1 Link/200–250 Wörter).
$own_stats = $this->get_link_stats( $post_id );
echo '📊 ' . esc_html( sprintf( _n( 'Dieser Beitrag verlinkt selbst aktuell auf %d anderen Beitrag', 'Dieser Beitrag verlinkt selbst aktuell auf %d andere Beiträge', $own_stats['links'], $D ), $own_stats['links'] ) );
echo ' (' . ( $own_stats['remaining'] > 0
? esc_html( sprintf( _n( 'noch %d sinnvoll, Ziel ~%d', 'noch %d sinnvoll, Ziel ~%d', $own_stats['remaining'], $D ), $own_stats['remaining'], $own_stats['recommended'] ) )
: esc_html__( 'Empfehlung erreicht', $D ) ) . ') .
';
if ( ! empty( $inbound['linking_from'] ) ) {
$count = count( $inbound['linking_from'] );
echo '✓ ' . esc_html( sprintf( _n( '%d Beitrag verlinkt bereits hierher:', '%d Beiträge verlinken bereits hierher:', $count, $D ), $count ) ) . '
';
echo '';
foreach ( $inbound['linking_from'] as $p ) {
$edit_link = get_edit_post_link( $p['id'] );
$stats = $this->get_link_stats( $p['id'] );
echo '';
if ( $edit_link ) { echo '' . esc_html( $p['title'] ) . ' '; }
else { echo '' . esc_html( $p['title'] ) . ' '; }
echo '🔗 ' . esc_html( $stats['links'] ) . '/' . esc_html( $stats['recommended'] ) . ' ';
echo ' ';
}
echo ' ';
} else {
echo '✗ ' . esc_html__( 'Noch kein anderer Beitrag verlinkt hierher.', $D ) . '
';
}
if ( ! empty( $inbound['missing_related'] ) ) {
echo '⚠ ' . esc_html__( 'Thematisch passend, verlinkt aber noch nicht:', $D ) . '
';
echo '';
foreach ( $inbound['missing_related'] as $p ) {
$edit_link = get_edit_post_link( $p['id'] );
$stats = $this->get_link_stats( $p['id'] );
echo '';
echo '' . esc_html( $p['title'] ) . ' 🔗 ' . esc_html( $stats['links'] ) . '/' . esc_html( $stats['recommended'] ) . ' ';
echo '';
// Immer sichtbarer Backlink-Button — unabhängig davon, ob der
// aktuelle Beitrag bereits interne Links hat. WICHTIG: Dieser
// Button bearbeitet NICHT den gerade offenen Beitrag, sondern
// fügt den Link im VORGESCHLAGENEN Beitrag ein (data-source-id),
// und zwar zurück auf den hier offenen Beitrag (data-target-id) —
// genau die Lücke, die diese Liste anzeigt.
echo '↗ ' . esc_html__( 'Backlink setzen', $D ) . ' ';
if ( $edit_link ) { echo '' . esc_html__( 'Bearbeiten', $D ) . ' '; }
echo ' ';
echo ' ';
}
echo ' ';
echo '' . esc_html__( '„Backlink setzen“ bearbeitet direkt den anderen Beitrag – nicht diesen hier.', $D ) . '
';
$i18n_js = wp_json_encode( array(
'reload' => __( 'Bitte Seite neu laden.', $D ),
'inserting' => __( 'Wird eingefügt...', $D ),
'done' => __( '✓ Gesetzt!', $D ),
'error' => __( '✗ Fehler', $D ),
) );
echo '';
}
}
public function add_toggle_metabox() {
foreach ( $this->get_opts()['enabled_post_types'] as $t ) {
add_meta_box( 'aije_toggle', esc_html__( 'AI JSON – Ausgabe', 'ai-json-extractor-auto-embed-lite' ), array( $this, 'render_toggle_metabox' ), $t, 'side', 'default' );
}
}
public function add_ki_highlight_metabox() {
foreach ( $this->get_opts()['enabled_post_types'] as $t ) {
add_meta_box( 'aije_ki_highlight', esc_html__( 'WP Rankify – KI-Suche', 'ai-json-extractor-auto-embed-lite' ), array( $this, 'render_ki_highlight_metabox' ), $t, 'side', 'high' );
}
}
public function render_ki_highlight_metabox( $post ) {
$active = get_post_meta( $post->ID, '_aije_ki_highlight', true );
$yoast = get_post_meta( $post->ID, '_yoast_wpseo_is_cornerstone', true );
$rankmath = get_post_meta( $post->ID, 'rank_math_pillar_content', true );
$via_seo = ! empty( $yoast ) || $rankmath === 'on';
wp_nonce_field( 'aije_ki_highlight', 'aije_ki_highlight_nonce' );
echo '';
echo ' ';
echo '' . esc_html__( 'KI-Suchen auf diesen Inhalt hinweisen', 'ai-json-extractor-auto-embed-lite' ) . ' ';
echo '
' . esc_html__( 'Dieser Beitrag wird in der llms.txt aufgeführt – der Datei die KI-Suchen wie Perplexity lesen, wenn sie deine Website analysieren.', 'ai-json-extractor-auto-embed-lite' ) . '
';
if ( $via_seo ) {
$via_label = ! empty( $yoast ) ? __( 'Yoast SEO (Cornerstone Content)', 'ai-json-extractor-auto-embed-lite' ) : __( 'Rank Math (Pillar Content)', 'ai-json-extractor-auto-embed-lite' );
echo '✓ ' . sprintf( esc_html__( 'Bereits markiert in %s.', 'ai-json-extractor-auto-embed-lite' ), esc_html( $via_label ) ) . '
';
}
}
public function render_toggle_metabox( $post ) {
$no = get_post_meta( $post->ID, self::META_NO_EMBED, true );
wp_nonce_field( 'aije_toggle', 'aije_toggle_nonce' );
echo ' ';
echo esc_html__( 'Ausgabe für diesen Beitrag deaktivieren', 'ai-json-extractor-auto-embed-lite' ) . ' ';
echo '' . esc_html__( 'Deaktiviert JSON-Embed und Person-Schema für diesen Beitrag.', 'ai-json-extractor-auto-embed-lite' ) . '
';
}
public function save_post_meta( $post_id ) {
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return; }
if ( ! current_user_can( 'edit_post', $post_id ) ) { return; }
// Toggle (JSON-Embed deaktivieren)
if ( isset( $_POST['aije_toggle_nonce'] ) && wp_verify_nonce( wp_unslash( $_POST['aije_toggle_nonce'] ), 'aije_toggle' ) ) {
$no = ! empty( $_POST['aije_no_embed'] ) ? '1' : '';
if ( $no ) { update_post_meta( $post_id, self::META_NO_EMBED, '1' ); }
else { delete_post_meta( $post_id, self::META_NO_EMBED ); }
}
// KI-Suche hervorheben
if ( isset( $_POST['aije_ki_highlight_nonce'] ) && wp_verify_nonce( wp_unslash( $_POST['aije_ki_highlight_nonce'] ), 'aije_ki_highlight' ) ) {
if ( ! empty( $_POST['aije_ki_highlight'] ) ) {
update_post_meta( $post_id, '_aije_ki_highlight', '1' );
} else {
delete_post_meta( $post_id, '_aije_ki_highlight' );
}
delete_transient( 'aije_llms_txt' );
}
}
public function add_preview_metabox() {
foreach ( $this->get_opts()['enabled_post_types'] as $t ) {
add_meta_box( 'aije_preview', esc_html__( 'AI JSON – Vorschau (readonly)', 'ai-json-extractor-auto-embed-lite' ), array( 'AIJE_Lite_100', 'render_preview_metabox_cb' ), $t, 'side', 'default' );
}
}
public static function render_preview_metabox_cb( $post ) {
$self = new self();
$data = $self->extract_payload( $post );
$flags = 0;
if ( defined( 'JSON_UNESCAPED_UNICODE' ) ) { $flags |= JSON_UNESCAPED_UNICODE; }
if ( defined( 'JSON_UNESCAPED_SLASHES' ) ) { $flags |= JSON_UNESCAPED_SLASHES; }
if ( defined( 'JSON_PRETTY_PRINT' ) ) { $flags |= JSON_PRETTY_PRINT; }
$json = function_exists( 'wp_json_encode' ) ? wp_json_encode( $data, $flags ) : json_encode( $data );
echo '';
$pto = get_post_type_object( $post->post_type );
$base = ( ! empty( $pto->rest_base ) ) ? $pto->rest_base : $post->post_type;
$api_url = rest_url( 'wp/v2/' . $base . '/' . $post->ID );
if ( ! empty( $api_url ) ) {
$url_out = ( $post->post_status === 'publish' ) ? $api_url : add_query_arg( 'context', 'edit', $api_url );
echo '' . esc_html__( 'REST API öffnen', 'ai-json-extractor-auto-embed-lite' ) . '
';
}
}
/* ===== SEO-Meta-Box ===== */
public function add_seo_metabox() {
$o = $this->get_opts();
$resolved = $this->get_active_seo_plugin();
if ( $resolved !== 'own' ) { return; }
$conflict = '';
if ( defined( 'WPSEO_VERSION' ) ) { $conflict = 'Yoast SEO'; }
if ( defined( 'RANK_MATH_VERSION' ) ) { $conflict = 'RankMath'; }
if ( defined( 'AIOSEO_VERSION' ) ) { $conflict = 'AIOSEO'; }
foreach ( $o['enabled_post_types'] as $t ) {
add_meta_box( 'aije_seo_meta', __( 'WP Rankify – SEO', 'ai-json-extractor-auto-embed-lite' ) . ( $conflict ? ' ⚠ ' . sprintf( __( 'Konflikt mit %s', 'ai-json-extractor-auto-embed-lite' ), $conflict ) : '' ), array( $this, 'render_seo_metabox' ), $t, 'normal', 'high' );
}
}
public function render_seo_metabox( $post ) {
wp_nonce_field( 'aije_seo_meta', 'aije_seo_meta_nonce' );
$conflict = '';
if ( defined( 'WPSEO_VERSION' ) ) { $conflict = 'Yoast SEO'; }
if ( defined( 'RANK_MATH_VERSION' ) ) { $conflict = 'RankMath'; }
if ( defined( 'AIOSEO_VERSION' ) ) { $conflict = 'AIOSEO'; }
if ( $conflict ) {
echo '⚠
' . sprintf( esc_html__( '%s ist noch aktiv.', 'ai-json-extractor-auto-embed-lite' ), esc_html( $conflict ) ) . ' ' . sprintf( esc_html__( 'Deaktiviere %s oder wähle in den', 'ai-json-extractor-auto-embed-lite' ), esc_html( $conflict ) ) . '
' . esc_html__( 'Einstellungen', 'ai-json-extractor-auto-embed-lite' ) . ' ' . esc_html__( '„Auto-Erkennung".', 'ai-json-extractor-auto-embed-lite' ) . '
';
}
$seo_title = get_post_meta( $post->ID, '_aije_seo_title', true );
$meta_desc = get_post_meta( $post->ID, '_aije_metadesc', true );
$robots = get_post_meta( $post->ID, '_aije_robots', true ) ?: 'index,follow';
$canonical = get_post_meta( $post->ID, '_aije_canonical', true ) ?: get_permalink( $post->ID );
$title_len = mb_strlen( $seo_title );
$desc_len = mb_strlen( $meta_desc );
$title_col = ( $title_len >= 50 && $title_len <= 65 ) ? '#22c55e' : ( $title_len > 0 ? '#f59e0b' : '#ef4444' );
$desc_col = ( $desc_len >= 120 && $desc_len <= 160 ) ? '#22c55e' : ( $desc_len > 0 ? '#f59e0b' : '#ef4444' );
echo '';
echo '
';
echo '
' . esc_html__( 'Meta-Description', 'ai-json-extractor-auto-embed-lite' ) . ' ' . sprintf( esc_html__( '%d / 160 Zeichen', 'ai-json-extractor-auto-embed-lite' ), $desc_len ) . '
';
if ( ! empty( $seo_title ) || ! empty( $meta_desc ) ) {
$purl = get_permalink( $post->ID );
echo '
' . esc_html__( 'Google-Vorschau', 'ai-json-extractor-auto-embed-lite' ) . '
' . esc_html( mb_substr( $seo_title ?: get_the_title( $post ), 0, 65 ) ) . '
' . esc_url( $purl ) . '
' . esc_html( mb_substr( $meta_desc, 0, 160 ) ) . '
';
}
echo '
Robots ';
foreach ( array( 'index,follow' => sprintf( __( 'index, follow (%s)', 'ai-json-extractor-auto-embed-lite' ), __( 'Standard', 'ai-json-extractor-auto-embed-lite' ) ), 'noindex,follow' => 'noindex, follow', 'index,nofollow' => 'index, nofollow', 'noindex,nofollow' => 'noindex, nofollow' ) as $val => $lbl ) {
echo '' . esc_html( $lbl ) . ' ';
}
echo '
' . esc_html__( 'Canonical URL', 'ai-json-extractor-auto-embed-lite' ) . '
';
echo '
';
?>
$id !== $post_id ) ) );
wp_send_json_success();
}
private function render_dashboard_list( $type, $filter = '' ) {
$types = $this->get_opts()['enabled_post_types'];
$excluded_ids = get_option( 'aije_excluded_posts', array() );
$posts = get_posts( array( 'post_type' => $types, 'posts_per_page' => -1, 'post_status' => 'publish', 'post__not_in' => $excluded_ids ?: array( 0 ) ) );
$colors = array( 'bad' => '#e74c3c', 'medium' => '#e67e22', 'good' => '#27ae60', 'filter' => '#0073aa' );
$filter_map = array(
'no_image' => array( 'no_featured_image' ),
'no_meta' => array( 'meta_desc_length_suboptimal', 'meta_desc_too_short', 'no_meta_desc' ),
'too_short' => array( 'content_too_short', 'content_slightly_short' ),
'no_eeat' => array( 'no_job_title' ),
'no_h2' => array( 'no_h2' ),
'no_internal' => array( 'no_internal_links' ),
); // Matching läuft über den stabilen 'key' aus trait-score.php, nicht über den (übersetzten) 'msg'-Text
echo '';
$found = false;
foreach ( $posts as $post ) {
$analysis = $this->calculate_ai_analysis( $post );
$score = $analysis['score'];
$show = false;
if ( $type === 'bad' && $score < 50 ) { $show = true; }
if ( $type === 'medium' && $score >= 50 && $score < 75 ) { $show = true; }
if ( $type === 'good' && $score >= 75 ) { $show = true; }
if ( $type === 'filter' ) { $show = true; }
if ( ! $show ) { continue; }
if ( ! empty( $filter ) && isset( $filter_map[ $filter ] ) ) {
$needle_keys = $filter_map[ $filter ];
$matched = false;
foreach ( $analysis['issues'] as $iss ) { if ( in_array( $iss['key'] ?? '', $needle_keys, true ) ) { $matched = true; break; } }
if ( ! $matched ) { continue; }
}
$found = true;
$top_issues = array_slice( $analysis['issues'], 0, 3 );
$sev_bg = array( 'high' => '#fdecea', 'medium' => '#fef9e7', 'low' => '#eafaf1' );
$cat_icons = array( 'content' => '✍', 'eeat' => '🏅', 'technical' => '⚙', 'linking' => '🔗' );
echo '';
echo '';
echo '
' . esc_html( $post->post_title ) . ' ';
if ( ! empty( $top_issues ) && empty( $filter ) ) {
echo '
';
foreach ( $top_issues as $issue ) {
$ic = $cat_icons[ $issue['category'] ] ?? '•';
$bg = $sev_bg[ $issue['severity'] ] ?? '#f5f5f5';
echo '
' . $ic . ' ' . esc_html( $issue['msg'] ) . '
';
}
echo '
';
}
echo '
';
echo '
' . esc_html( $score ) . ' ';
echo '
🗑 ';
echo '
';
}
if ( ! $found ) { echo '' . esc_html__( 'Keine Artikel in dieser Kategorie.', 'ai-json-extractor-auto-embed-lite' ) . ' '; }
echo ' ';
}
private function render_exclude_js() { ?>