get_supported_post_types();
$current_type = sanitize_key($this->get_query_value('seo_post_type', 'all'));
if ('all' !== $current_type && !in_array($current_type, $post_types, true)) {
$current_type = 'all';
}
$score_bucket = sanitize_key($this->get_query_value('seo_score_bucket'));
$issue_filter = sanitize_key($this->get_query_value('seo_issue'));
$search_term = $this->get_query_value('s');
$paged = max(1, $this->get_query_int('paged', 1));
$per_page = isset($settings['dashboard_page_size']) ? max(10, (int) $settings['dashboard_page_size']) : 20;
$query = $this->get_filtered_query($post_types, $current_type, $score_bucket, $issue_filter, $search_term, $paged, $per_page);
$stats = $this->get_dashboard_stats($post_types);
$base_url = admin_url('tools.php?page=kbyte-seo-workbench');
$query_posts = (isset($query->posts) && is_array($query->posts)) ? $query->posts : array();
$max_num_pages = (isset($query->max_num_pages) && is_numeric($query->max_num_pages)) ? (int) $query->max_num_pages : 1;
?>
add_query_arg('paged', '%#%'),
'format' => '',
'current' => $paged,
'total' => max(1, $max_num_pages),
'type' => 'array',
));
if (!empty($pagination)) :
?>
0,
'average_score' => 0,
'healthy_items' => 0,
'orphan_items' => 0,
);
}
$ids = get_posts(array(
'post_type' => $post_types,
'post_status' => 'publish',
'posts_per_page' => -1,
'fields' => 'ids',
'orderby' => 'ID',
'order' => 'ASC',
));
$total_items = count($ids);
$total_score = 0;
$scored_items = 0;
$healthy_items = 0;
$orphan_items = 0;
if (!empty($ids)) {
update_meta_cache('post', $ids);
foreach ($ids as $post_id) {
$score = get_post_meta($post_id, '_KSWB_Analyzer_score', true);
if ('' !== $score && is_numeric($score)) {
$score = (int) $score;
$total_score += $score;
$scored_items++;
if ($score >= 80) {
$healthy_items++;
}
}
if ('1' === get_post_meta($post_id, '_KSWB_Analyzer_is_orphan', true)) {
$orphan_items++;
}
}
}
return array(
'total_items' => $total_items,
'average_score' => $scored_items > 0 ? (int) round($total_score / $scored_items) : 0,
'healthy_items' => $healthy_items,
'orphan_items' => $orphan_items,
);
}
private function get_filtered_query($post_types, $current_type, $score_bucket, $issue_filter, $search_term, $paged, $per_page) {
$candidate_ids = get_posts(array(
'post_type' => ('all' === $current_type) ? $post_types : array($current_type),
'post_status' => 'publish',
'posts_per_page' => -1,
'fields' => 'ids',
'orderby' => 'modified',
'order' => 'DESC',
's' => $search_term,
));
$filtered_ids = array();
if (!empty($candidate_ids)) {
update_meta_cache('post', $candidate_ids);
foreach ($candidate_ids as $post_id) {
if (!$this->matches_score_bucket($post_id, $score_bucket)) {
continue;
}
if (!$this->matches_issue_filter($post_id, $issue_filter)) {
continue;
}
$filtered_ids[] = (int) $post_id;
}
}
$total = count($filtered_ids);
$offset = max(0, ($paged - 1) * $per_page);
$page_ids = array_slice($filtered_ids, $offset, $per_page);
$posts = array();
if (!empty($page_ids)) {
$posts = get_posts(array(
'post_type' => ('all' === $current_type) ? $post_types : array($current_type),
'post_status' => 'publish',
'post__in' => $page_ids,
'posts_per_page' => count($page_ids),
'orderby' => 'post__in',
));
}
return (object) array(
'posts' => $posts,
'max_num_pages' => max(1, (int) ceil($total / $per_page)),
);
}
private function matches_score_bucket($post_id, $score_bucket) {
if ('' === $score_bucket) {
return true;
}
$score = get_post_meta($post_id, '_KSWB_Analyzer_score', true);
if ('' === $score || !is_numeric($score)) {
return ('unanalyzed' === $score_bucket);
}
$score = (int) $score;
if ('good' === $score_bucket) {
return $score >= 80;
}
if ('medium' === $score_bucket) {
return ($score >= 60 && $score <= 79);
}
if ('poor' === $score_bucket) {
return $score < 60;
}
return true;
}
private function matches_issue_filter($post_id, $issue_filter) {
if ('' === $issue_filter) {
return true;
}
$map = array(
'missing_meta_title' => array('_KSWB_Analyzer_has_meta_title', '0'),
'missing_meta_description' => array('_KSWB_Analyzer_has_meta_description', '0'),
'orphan' => array('_KSWB_Analyzer_is_orphan', '1'),
'cornerstone' => array('_KSWB_Analyzer_cornerstone_needs_work', '1'),
'broken_links' => array('_KSWB_Analyzer_has_broken_links', '1'),
'noindex' => array('_KSWB_Analyzer_is_noindex', '1'),
);
if (!isset($map[$issue_filter])) {
return true;
}
return get_post_meta($post_id, $map[$issue_filter][0], true) === $map[$issue_filter][1];
}
private function get_query_value($key, $default = '') {
$value = filter_input(INPUT_GET, $key, FILTER_UNSAFE_RAW);
return is_string($value) ? sanitize_text_field(wp_unslash($value)) : $default;
}
private function get_query_int($key, $default = 0) {
$value = filter_input(INPUT_GET, $key, FILTER_VALIDATE_INT);
return (false !== $value && null !== $value) ? absint($value) : $default;
}
private function get_admin_edit_link($post_id) {
$post_id = absint($post_id);
if ($post_id <= 0) {
return admin_url('edit.php');
}
return add_query_arg(
array(
'post' => $post_id,
'action' => 'edit',
),
admin_url('post.php')
);
}
private function get_post_type_label($post_type) {
$object = get_post_type_object($post_type);
if ($object && isset($object->labels->singular_name) && is_string($object->labels->singular_name) && '' !== $object->labels->singular_name) {
return $object->labels->singular_name;
}
if ($object && isset($object->label) && is_string($object->label) && '' !== $object->label) {
return $object->label;
}
return ucwords(str_replace(array('-', '_'), ' ', (string) $post_type));
}
private function get_post_display_title($post_id) {
$title = get_the_title($post_id);
if (is_string($title) && '' !== trim($title)) {
return $title;
}
return sprintf(
/* translators: %d: post ID. */
__('Untitled (#%d)', 'kbyte-seo-workbench'),
(int) $post_id
);
}
private function render_score_badge($score) {
if ('' === $score || !is_numeric($score)) {
return '' . esc_html__('Not analyzed', 'kbyte-seo-workbench') . '';
}
$score = (int) $score;
return '' . esc_html($score . '/100') . '';
}
private function get_issue_label($issue) {
$labels = array(
'missing_meta_title' => __('Missing meta title', 'kbyte-seo-workbench'),
'missing_meta_description' => __('Missing meta description', 'kbyte-seo-workbench'),
'missing_focus_keyword' => __('Missing focus keyword', 'kbyte-seo-workbench'),
'orphan' => __('Orphan page', 'kbyte-seo-workbench'),
'broken_links' => __('Broken outgoing links', 'kbyte-seo-workbench'),
'noindex' => __('Noindex enabled', 'kbyte-seo-workbench'),
'cornerstone' => __('Cornerstone content', 'kbyte-seo-workbench'),
'thin_content' => __('Thin content', 'kbyte-seo-workbench'),
'keyword_placement' => __('Keyword placement', 'kbyte-seo-workbench'),
'keyword_density' => __('Keyword density', 'kbyte-seo-workbench'),
'links' => __('Link coverage', 'kbyte-seo-workbench'),
'canonical' => __('Canonical issue', 'kbyte-seo-workbench'),
'readability' => __('Readability', 'kbyte-seo-workbench'),
'healthy' => __('Healthy', 'kbyte-seo-workbench'),
'general' => __('General improvements', 'kbyte-seo-workbench'),
);
return isset($labels[$issue]) ? $labels[$issue] : __('Not analyzed', 'kbyte-seo-workbench');
}
private function get_score_color($score) {
if ($score >= 80) {
return '#1f9d55';
}
if ($score >= 60) {
return '#d97706';
}
return '#dc2626';
}
}