settings->ready() && $post && $post->post_status === 'publish' && in_array($post->post_type, $this->settings->all()['post_types'], true) && !post_password_required($post) && !is_feed() && !is_preview() && !is_admin(); } public function thread(WP_Post $post): string { $saved = get_post_meta($post->ID, '_kommentti_thread_id', true); return is_string($saved) && $saved !== '' ? $saved : (string) $post->ID; } public function render(?WP_Post $post = null): string { $post ??= get_post(); if ($this->rendered || !$this->eligible($post) || !is_singular() || (int) get_queried_object_id() !== $post->ID) { return ''; } $this->rendered = true; $settings = $this->settings->all(); $thread = $this->thread($post); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public read-only pagination must also work for crawlers. $page = filter_var(wp_unslash($_GET['comments_page'] ?? 1), FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'max_range' => 10000]]) ?: 1; $snapshot = $settings['mode'] === 'server' ? $this->api->comments($post->ID, $thread, $page) : null; $readOnly = !comments_open($post->ID); $this->embed = ['data-site' => $settings['site_id'], 'data-thread' => $thread, 'data-target' => 'kommentti-comments', 'data-locale' => $this->settings->locale(), 'data-readonly' => $readOnly ? 'true' : 'false']; if ($snapshot !== null) { $this->embed += ['data-view' => 'composer', 'data-render-target' => 'kommentti-server-comments']; } wp_enqueue_style('kommentti-publisher'); wp_enqueue_script('kommentti-embed'); ob_start(); if (!wp_style_is('kommentti-publisher', 'done')) { wp_print_styles(['kommentti-publisher']); } echo '
'; echo '

'.esc_html__('Comments', 'kommentti').'

'; if ($snapshot !== null) { // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- markup() escapes every dynamic HTML value. echo $this->markup($snapshot, get_permalink($post), $readOnly || $snapshot['locked']); } echo '
'; return (string) ob_get_clean(); } public function script_tag(string $tag, string $handle): string { if ($handle !== 'kommentti-embed' || !$this->embed) { return $tag; } return wp_get_script_tag(['id' => 'kommentti-embed-js', 'src' => Settings::API_URL.'/embed.js?v=6', 'defer' => true, ...$this->embed]); } public function markup(array $snapshot, string $permalink, bool $readOnly = false): string { $labels = ['empty' => __('No published comments yet. Be the first to join the conversation.', 'kommentti'), 'reply' => __('Reply', 'kommentti'), 'report' => __('Report', 'kommentti'), 'reply-badge' => __('Reply to a comment', 'kommentti'), 'previous' => __('Newer comments', 'kommentti'), 'next' => __('Older comments', 'kommentti'), 'locale' => $this->settings->locale()]; $html = '
$label) { $html .= ' data-'.esc_attr($name).'="'.esc_attr($label).'"'; } $html .= '>

'.esc_html__('Comments', 'kommentti').': '.(int) $snapshot['total'].'

    '; foreach ($snapshot['comments'] as $comment) { $html .= '
  1. '.esc_html($comment['name']).'
    '; if (!empty($comment['parent_id'])) { $html .= ''.esc_html($labels['reply-badge']).''; } $html .= '

    '.esc_html($comment['body']).'

    '; foreach (['reply', 'report'] as $action) { if ($action === 'reply' && $readOnly) { continue; } $html .= ''; } $html .= '
  2. '; } $html .= '

'.esc_html($labels['empty']).'

'; if ($snapshot['page'] > 1 || $snapshot['has_more']) { $html .= ''; } return $html.'
'; } }