'GET', 'callback' => [$this, 'get_posts'], 'permission_callback' => [$this, 'check_permission']]); register_rest_route('linkbraid/v1', '/suggestions/(?P\d+)', ['methods' => 'GET', 'callback' => [$this, 'get_suggestions'], 'permission_callback' => [$this, 'check_permission']]); } public function check_permission() { return current_user_can('manage_options'); } public function get_posts($request) { global $wpdb; $page = max(1, (int) $request->get_param('page')); $size = max(1, min(100, (int) ($request->get_param('per_page') ?: 20))); $types = lbraid_post_types(); $params = $types; $where = "p.post_status='publish' AND p.post_type IN (" . implode(',', array_fill(0, count($types), '%s')) . ')'; $search = sanitize_text_field($request->get_param('search') ?? ''); if ($search !== '') { $where .= ' AND p.post_title LIKE %s'; $params[] = '%' . $wpdb->esc_like($search) . '%'; } $filter = sanitize_key($request->get_param('filter') ?? 'all'); if ($filter === 'priority') $where .= ' AND i.is_priority=1'; elseif ($filter === 'has_suggestions') $where .= ' AND COALESCE(s.cnt,0)>0'; elseif ($filter === 'no_outgoing') $where .= " AND COALESCE(o.cnt,0)=0 AND EXISTS (SELECT 1 FROM {$wpdb->postmeta} m WHERE m.post_id=p.ID AND m.meta_key='_lbraid_links_scanned')"; elseif ($filter === 'no_incoming') $where .= ' AND COALESCE(n.cnt,0)=0'; elseif ($filter === 'stale') $where .= " AND (i.embedding IS NULL OR EXISTS (SELECT 1 FROM {$wpdb->postmeta} m WHERE m.post_id=p.ID AND m.meta_key='_lbraid_index_stale' AND m.meta_value='1'))"; elseif (strpos($filter, 'type_') === 0) { $where .= ' AND p.post_type=%s'; $params[] = substr($filter, 5); } $links = $wpdb->prefix . 'lbraid_existing_links'; $from = "FROM {$wpdb->posts} p LEFT JOIN {$wpdb->prefix}lbraid_post_index i ON i.post_id=p.ID LEFT JOIN (SELECT post_id, MAX(meta_value) meta_value FROM {$wpdb->postmeta} WHERE meta_key='_lbraid_index_stale' GROUP BY post_id) st ON st.post_id=p.ID LEFT JOIN (SELECT source_post_id,COUNT(*) cnt FROM $links GROUP BY source_post_id) o ON o.source_post_id=p.ID LEFT JOIN (SELECT target_post_id,COUNT(*) cnt FROM $links GROUP BY target_post_id) n ON n.target_post_id=p.ID LEFT JOIN (SELECT source_post_id,COUNT(*) cnt FROM {$wpdb->prefix}lbraid_link_suggestions WHERE status='pending' GROUP BY source_post_id) s ON s.source_post_id=p.ID"; $total = (int) $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) $from WHERE $where", ...$params)); $dir = strtolower($request->get_param('sort_dir') ?? '') === 'desc' ? 'DESC' : 'ASC'; $sort = $request->get_param('sort_by'); // Every column of the Knots table sorts; the whitelist is the contract. $sorts = [ 'in' => 'COALESCE(n.cnt,0)', 'out' => 'COALESCE(o.cnt,0)', 'pending' => 'COALESCE(s.cnt,0)', 'priority' => 'COALESCE(i.is_priority,0)', 'title' => 'p.post_title', 'state' => "(CASE WHEN i.embedding IS NULL THEN 0 WHEN st.meta_value='1' THEN 1 ELSE 2 END)", ]; $order = isset($sorts[$sort]) ? $sorts[$sort] . " $dir," : ''; $params[] = $size; $params[] = ($page - 1) * $size; $rows = $wpdb->get_results($wpdb->prepare("SELECT p.*,i.embedding IS NOT NULL AS indexed,i.is_priority,i.priority_weight,i.last_indexed,COALESCE(o.cnt,0) outgoing_links,COALESCE(n.cnt,0) incoming_links,COALESCE(s.cnt,0) pending_suggestions $from WHERE $where ORDER BY $order p.post_date DESC,p.ID DESC LIMIT %d OFFSET %d", ...$params)); $posts = []; foreach ($rows as $p) $posts[] = ['id' => (int) $p->ID, 'title' => $p->post_title, 'url' => get_permalink($p->ID), 'type' => $p->post_type, 'editor' => Linkbraid_Content::editor($p->ID), 'indexed' => (bool) $p->indexed, 'stale' => (bool) get_post_meta($p->ID, '_lbraid_index_stale', true), 'scanned' => (bool) get_post_meta($p->ID, '_lbraid_links_scanned', true), 'is_priority' => (bool) $p->is_priority, 'priority_weight' => (int) $p->priority_weight, 'outgoing_links' => (int) $p->outgoing_links, 'incoming_links' => (int) $p->incoming_links, 'pending_suggestions' => (int) $p->pending_suggestions, 'last_indexed' => $p->last_indexed]; return new WP_REST_Response(['posts' => $posts, 'total' => $total, 'total_pages' => (int) ceil($total / $size)], 200); } public function get_suggestions($request) { return new WP_REST_Response(Linkbraid_Database::get_suggestions_for_post(absint($request['post_id'])), 200); } }