' . seolok_brand() . '›
' . esc_html($title) . '
';
}
/** A panel with an icon, a title, small print and an optional "see all" link. */
function seolok_ui_panel_open($icon, $title, $lead = '', $more_url = '', $more_label = '', $badge = 'info', $id = '') {
$o = '';
}
/** One small number tile (label, value, line under it). */
function seolok_ui_stat($icon, $tone, $label, $value, $sub = '') {
return '' . seolok_ui_icon($icon) . '' . esc_html($label) . '' . esc_html($value) . '' . esc_html($sub) . '
';
}
/** A number SEOLOK may not have: "—" for null. */
function seolok_maybe_num($v, $decimals = 0) {
if (!is_numeric($v)) {
return '—';
}
return $decimals > 0 ? number_format((float) $v, $decimals, seolok_is_tr() ? ',' : '.', seolok_is_tr() ? '.' : ',') : seolok_num($v);
}
/** Draws one insight screen: top bar, SEOLOK's answer (or why there is none), the site note. */
function seolok_insight_page($name) {
if (!current_user_can('manage_options') || !in_array($name, SEOLOK_INSIGHT_SCREENS, true)) {
return;
}
$force = isset($_GET['seolok_refresh']); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- a read-only "fetch again" flag
$data = seolok_is_configured() ? seolok_insight_data($name, $force) : new WP_Error('seolok_not_configured', 'Not configured');
$panel = !is_wp_error($data) ? seolok_panel_url(isset($data['panel']) ? $data['panel'] : '') : '';
$o = seolok_screen_open();
$o .= seolok_ui_topbar(seolok_s('menu_' . $name), add_query_arg(array('page' => seolok_insight_slug($name), 'seolok_refresh' => '1'), admin_url('admin.php')), $panel);
$o .= '' . esc_html(seolok_s('in_lead_' . $name)) . '
';
if (is_wp_error($data)) {
$o .= seolok_render_problem($data);
} else {
$render = 'seolok_insight_' . $name;
$o .= $render(seolok_arr(isset($data['data']) ? $data['data'] : null));
}
$o .= seolok_site_note() . '';
echo wp_kses($o, seolok_allowed_html());
}
/** A pill for a rank change: "▲ 3" better, "▼ 2" worse, "—" none. */
function seolok_rank_change($change) {
if (!is_numeric($change) || (int) $change === 0) {
return '—';
}
return seolok_delta((string) abs((int) $change), (int) $change > 0);
}
/** An empty state: an icon, one sentence, and what to do (optional link to SEOLOK). */
function seolok_ui_empty($text, $url = '', $label = '') {
return '';
}
/* ------------------------------------------------------------- Site analysis */
function seolok_insight_audit($d) {
if (empty($d['scanned'])) {
return '' . seolok_ui_empty(seolok_s('in_audit_never')) . '';
}
$counts = seolok_arr(isset($d['counts']) ? $d['counts'] : null);
$score = isset($d['score']) && is_numeric($d['score']) ? (int) $d['score'] : null;
$crit = isset($counts['critical']) ? (int) $counts['critical'] : 0;
$warn = isset($counts['warning']) ? (int) $counts['warning'] : 0;
$info = isset($counts['info']) ? (int) $counts['info'] : 0;
$o = '';
$o .= seolok_ui_stat('chart', $score === null ? 'muted' : ($score >= 80 ? 'ok' : ($score >= 60 ? 'warn' : 'bad')), seolok_s('ov_score'), $score === null ? '—' : $score . ' / 100', sprintf(seolok_s('ov_score_sub'), seolok_str(isset($d['crawledAt']) ? $d['crawledAt'] : '')));
$o .= seolok_ui_stat('file', 'info', seolok_s('ov_pages'), seolok_maybe_num(isset($d['pages']) ? $d['pages'] : null), sprintf(seolok_s('in_healthy'), seolok_maybe_num(isset($d['healthy']) ? $d['healthy'] : null)));
$o .= seolok_ui_stat('warn', $crit > 0 ? 'bad' : 'ok', seolok_s('in_critical'), seolok_num($crit), seolok_s('in_critical_sub'));
$o .= seolok_ui_stat('bell', $warn > 0 ? 'warn' : 'ok', seolok_s('in_warnings'), seolok_num($warn), sprintf(seolok_s('in_info'), seolok_num($info)));
$o .= '
';
$issues = seolok_arr(isset($d['issues']) ? $d['issues'] : null);
$o .= seolok_ui_panel_open('list', seolok_s('in_issues'), seolok_s('in_issues_lead'));
if (!$issues) {
$o .= seolok_ui_note('ok', seolok_s('in_no_issues'));
} else {
$o .= '';
foreach ($issues as $g) {
$g = seolok_arr($g);
$sev = isset($g['severity']) && in_array($g['severity'], array('critical', 'warning', 'info'), true) ? $g['severity'] : 'info';
$tone = $sev === 'critical' ? 'bad' : ($sev === 'warning' ? 'warn' : 'info');
$urls = '';
foreach (seolok_arr(isset($g['urls']) ? $g['urls'] : null) as $u) {
$urls .= '' . esc_html(seolok_str($u)) . ' ';
}
$count = isset($g['count']) ? (int) $g['count'] : 0;
$o .= '' . seolok_tone_icon($tone) . '' . esc_html(seolok_str(isset($g['title']) ? $g['title'] : '')) . '' . seolok_pill($tone, sprintf(seolok_s('n_pages'), $count)) . '
' . esc_html(seolok_str(isset($g['why']) ? $g['why'] : '')) . '
' . seolok_ui_note('warn', seolok_str(isset($g['fix']) ? $g['fix'] : '')) . ($urls !== '' ? '
' : '') . ($count > count(seolok_arr(isset($g['urls']) ? $g['urls'] : null)) ? '
' . esc_html(sprintf(seolok_s('in_more_pages'), $count - count(seolok_arr($g['urls'])))) . '
' : '') . '
';
}
$o .= '
';
}
return $o . '';
}
/* ------------------------------------------------------------- Content opportunities */
function seolok_insight_opportunities($d) {
$items = seolok_arr(isset($d['items']) ? $d['items'] : null);
$o = '';
foreach (seolok_arr(isset($d['byType']) ? $d['byType'] : null) as $type => $t) {
$t = seolok_arr($t);
$o .= seolok_ui_stat($type === 'content_gap' ? 'file' : ($type === 'ctr_gap' ? 'eye' : ($type === 'cannibalization' ? 'chain' : 'chart')), 'info', seolok_str(isset($t['label']) ? $t['label'] : ''), seolok_maybe_num(isset($t['count']) ? $t['count'] : 0), '');
}
$o .= '
' . seolok_ui_panel_open('star', seolok_s('ov_opps'), seolok_str(isset($d['lastScanAt']) ? sprintf(seolok_s('in_scanned'), $d['lastScanAt']) : ''), '', '', 'warn');
if (!$items) {
return $o . seolok_ui_empty(seolok_s('ov_opps_none')) . '';
}
$o .= '| ' . esc_html(seolok_s('in_col_search')) . ' | ' . esc_html(seolok_s('in_col_kind')) . ' | ' . esc_html(seolok_s('al_col_page')) . ' | ' . esc_html(seolok_s('ov_impressions')) . ' | ' . esc_html(seolok_s('ov_position')) . ' |
';
foreach ($items as $i) {
$i = seolok_arr($i);
$o .= '| ' . esc_html(seolok_str(isset($i['query']) ? $i['query'] : '')) . '' . (!empty($i['inProgress']) ? ' ' . seolok_pill('info', seolok_s('in_in_progress')) : '') . ' | ' . seolok_pill('muted', seolok_str(isset($i['typeLabel']) ? $i['typeLabel'] : '')) . ' | ' . (!empty($i['page']) ? '' . esc_html(seolok_str($i['page'])) . '' : '—') . ' | ' . esc_html(seolok_maybe_num(isset($i['impressions']) ? $i['impressions'] : null)) . ' | ' . esc_html(seolok_maybe_num(isset($i['position']) ? $i['position'] : null, 1)) . ' |
';
}
return $o . '
';
}
/* ------------------------------------------------------------- Keywords */
function seolok_insight_keywords($d) {
$items = seolok_arr(isset($d['items']) ? $d['items'] : null);
$top3 = 0;
$top10 = 0;
$up = 0;
$down = 0;
foreach ($items as $k) {
$r = isset($k['rank']) && is_numeric($k['rank']) ? (int) $k['rank'] : null;
$top3 += $r !== null && $r <= 3 ? 1 : 0;
$top10 += $r !== null && $r <= 10 ? 1 : 0;
$c = isset($k['change']) && is_numeric($k['change']) ? (int) $k['change'] : 0;
$up += $c > 0 ? 1 : 0;
$down += $c < 0 ? 1 : 0;
}
$o = '';
$o .= seolok_ui_stat('search', 'info', seolok_s('in_tracked'), seolok_maybe_num(isset($d['tracked']) ? $d['tracked'] : count($items)), sprintf(seolok_s('in_cap'), seolok_maybe_num(isset($d['cap']) ? $d['cap'] : null)));
$o .= seolok_ui_stat('star', 'ok', seolok_s('in_top3'), seolok_num($top3), '');
$o .= seolok_ui_stat('chart', 'ok', seolok_s('in_top10'), seolok_num($top10), '');
$o .= seolok_ui_stat('refresh', $down > $up ? 'warn' : 'ok', seolok_s('in_moves'), '▲ ' . $up . ' ▼ ' . $down, seolok_s('in_moves_sub'));
$o .= '
' . seolok_ui_panel_open('search', seolok_s('menu_keywords'), seolok_s('in_kw_lead'));
if (!$items) {
return $o . seolok_ui_empty(seolok_s('in_kw_none')) . '';
}
$o .= '| ' . esc_html(seolok_s('in_col_keyword')) . ' | ' . esc_html(seolok_s('in_col_rank')) . ' | ' . esc_html(seolok_s('in_col_change')) . ' | ' . esc_html(seolok_s('in_col_volume')) . ' | ' . esc_html(seolok_s('in_col_gsc')) . ' | ' . esc_html(seolok_s('al_col_page')) . ' |
';
foreach ($items as $k) {
$k = seolok_arr($k);
$rank = isset($k['rank']) && is_numeric($k['rank']) ? (int) $k['rank'] : null;
$o .= '| ' . esc_html(seolok_str(isset($k['keyword']) ? $k['keyword'] : '')) . ' ' . esc_html(seolok_s(isset($k['device']) && $k['device'] === 'desktop' ? 'in_desktop' : 'in_mobile')) . '' . (!empty($k['unverified']) ? ' ' . seolok_pill('warn', seolok_s('in_unverified')) : '') . ' | ' . esc_html($rank === null ? seolok_s('in_not_found') : (string) $rank) . ' | ' . seolok_rank_change(isset($k['change']) ? $k['change'] : null) . ' | ' . esc_html(seolok_maybe_num(isset($k['volume']) ? $k['volume'] : null)) . ' | ' . esc_html(seolok_maybe_num(isset($k['gsc']) ? $k['gsc'] : null, 1)) . ' | ' . (!empty($k['url']) ? '' . esc_html(seolok_str($k['url'])) . '' : '—') . ' |
';
}
return $o . '
';
}
/* ------------------------------------------------------------- Competitors */
function seolok_insight_competitors($d) {
$items = seolok_arr(isset($d['items']) ? $d['items'] : null);
$o = seolok_ui_panel_open('chart', seolok_s('menu_competitors'), seolok_s('in_comp_lead'));
if (!$items) {
return $o . seolok_ui_empty(seolok_s('in_comp_none')) . '';
}
$o .= '| ' . esc_html(seolok_s('in_col_site')) . ' | ' . esc_html(seolok_s('in_col_shared')) . ' | ' . esc_html(seolok_s('in_col_top10')) . ' | ' . esc_html(seolok_s('in_col_avg')) . ' | ' . esc_html(seolok_s('in_col_wins')) . ' |
';
foreach ($items as $c) {
$c = seolok_arr($c);
$wins = isset($c['wins']) ? (int) $c['wins'] : 0;
$losses = isset($c['losses']) ? (int) $c['losses'] : 0;
$o .= '| ' . esc_html(seolok_str(isset($c['domain']) ? $c['domain'] : '')) . '' . (!empty($c['added']) ? ' ' . seolok_pill('info', seolok_s('in_added')) : '') . ' | ' . esc_html(seolok_maybe_num(isset($c['shared']) ? $c['shared'] : null)) . ' | ' . esc_html(seolok_maybe_num(isset($c['top10']) ? $c['top10'] : null)) . ' | ' . esc_html(seolok_maybe_num(isset($c['avgPosition']) ? $c['avgPosition'] : null, 1)) . ' | ' . esc_html(sprintf(seolok_s('in_you_ahead'), $wins)) . ' · ' . esc_html(sprintf(seolok_s('in_they_ahead'), $losses)) . ' |
';
}
return $o . '
';
}
/* ------------------------------------------------------------- Backlinks */
function seolok_insight_backlinks($d) {
// incoming links are measured for the Agency and Agency Pro plans only: SEOLOK sends no data for the others
if (!empty($d['locked'])) {
return seolok_ui_panel_open('chain', seolok_s('in_bl_list'), seolok_s('in_bl_lead')) . seolok_ui_empty(seolok_s('in_bl_locked')) . '';
}
$pct = function ($now, $prev) {
return is_numeric($now) && is_numeric($prev) && (int) $prev > 0 && (int) $now !== (int) $prev ? sprintf(seolok_s('in_since_last'), ((int) $now > (int) $prev ? '+' : '') . seolok_num((int) $now - (int) $prev)) : '';
};
$o = '';
$o .= seolok_ui_stat('shield', 'info', seolok_s('in_domain_score'), isset($d['score']) && is_numeric($d['score']) ? (int) $d['score'] . ' / 100' : '—', seolok_str(isset($d['checkedAt']) ? sprintf(seolok_s('in_checked'), $d['checkedAt']) : seolok_s('in_bl_never')));
$o .= seolok_ui_stat('chain', 'ok', seolok_s('in_ref_domains'), seolok_maybe_num(isset($d['referringDomains']) ? $d['referringDomains'] : null), $pct(isset($d['referringDomains']) ? $d['referringDomains'] : null, isset($d['referringPrev']) ? $d['referringPrev'] : null));
$o .= seolok_ui_stat('link', 'ok', seolok_s('in_backlinks'), seolok_maybe_num(isset($d['backlinks']) ? $d['backlinks'] : null), $pct(isset($d['backlinks']) ? $d['backlinks'] : null, isset($d['backlinksPrev']) ? $d['backlinksPrev'] : null));
$o .= seolok_ui_stat('warn', isset($d['lost30d']) && (int) $d['lost30d'] > 0 ? 'warn' : 'ok', seolok_s('in_lost'), seolok_maybe_num(isset($d['lost30d']) ? $d['lost30d'] : null), seolok_s('in_lost_sub'));
$o .= '
';
$items = seolok_arr(isset($d['items']) ? $d['items'] : null);
$o .= seolok_ui_panel_open('chain', seolok_s('in_bl_list'), seolok_s('in_bl_lead')) . seolok_sparkline(isset($d['trend']) ? $d['trend'] : array(), 'info');
if (!$items) {
return $o . seolok_ui_empty(seolok_s('in_bl_none')) . '';
}
$o .= '| ' . esc_html(seolok_s('in_col_from')) . ' | ' . esc_html(seolok_s('in_col_anchor')) . ' | ' . esc_html(seolok_s('al_col_page')) . ' | ' . esc_html(seolok_s('in_col_strength')) . ' | ' . esc_html(seolok_s('in_col_first')) . ' |
';
foreach ($items as $b) {
$b = seolok_arr($b);
$source = seolok_str(isset($b['source']) ? $b['source'] : '');
$host = wp_parse_url($source, PHP_URL_HOST);
$o .= '| ' . esc_html($host ? $host : $source) . '' . (isset($b['dofollow']) && $b['dofollow'] === false ? ' ' . seolok_pill('muted', 'nofollow') : '') . ' | ' . esc_html(seolok_str(isset($b['anchor']) ? $b['anchor'] : '')) . ' | ' . esc_html(seolok_str(isset($b['target']) ? $b['target'] : '')) . ' | ' . esc_html(seolok_maybe_num(isset($b['strength']) ? $b['strength'] : null)) . ' | ' . esc_html(seolok_str(isset($b['firstSeen']) ? $b['firstSeen'] : '')) . ' |
';
}
return $o . '
';
}
/* ------------------------------------------------------------- Reports */
function seolok_insight_reports($d) {
$items = seolok_arr(isset($d['items']) ? $d['items'] : null);
$o = seolok_ui_panel_open('file', seolok_s('menu_reports'), seolok_s('in_rep_lead'));
if (!$items) {
return $o . seolok_ui_empty(seolok_s('in_rep_none')) . '';
}
$o .= '';
foreach ($items as $r) {
$r = seolok_arr($r);
$from = seolok_str(isset($r['from']) ? $r['from'] : '');
$to = seolok_str(isset($r['to']) ? $r['to'] : '');
$o .= '- ' . seolok_ui_icon('file') . '' . esc_html(sprintf(seolok_s('in_rep_title'), seolok_str(isset($r['period']) ? $r['period'] : ''))) . '' . esc_html($from !== '' && $to !== '' ? $from . ' – ' . $to : seolok_str(isset($r['createdAt']) ? $r['createdAt'] : '')) . '' . seolok_pill(!empty($r['ready']) ? 'ok' : 'muted', seolok_s(!empty($r['ready']) ? 'in_ready' : 'in_preparing')) . '
';
}
return $o . '
' . esc_html(seolok_s('in_rep_open')) . '
';
}