diff --git a/includes/core/class-post-assembler.php b/includes/core/class-post-assembler.php new file mode 100644 index 0000000..8d1c705 --- /dev/null +++ b/includes/core/class-post-assembler.php @@ -0,0 +1,172 @@ + 'Guides', + 'solution' => 'Solutions', + 'comparison' => 'Comparisons', + 'article' => 'Articles', + ]; + + $category_name = $type_category_map[ $idea->get_article_type() ] ?? 'Articles'; + $category = get_term_by( 'name', $category_name, 'category' ); + + if ( ! $category ) { + $result = wp_insert_term( $category_name, 'category' ); + if ( ! is_wp_error( $result ) ) { + wp_set_post_categories( $post_id, [ $result['term_id'] ] ); + } + } else { + wp_set_post_categories( $post_id, [ $category->term_id ] ); + } + + $keywords = $idea->get_target_keywords(); + if ( ! empty( $keywords ) ) { + wp_set_post_tags( $post_id, $keywords, true ); + } + } + + /** + * Link generation log entries to the newly created post. + * Uses run_id for precise linking; falls back to timestamp for legacy data. + * + * @param int $post_id Post ID. + * @param string|null $run_id Pipeline run identifier, or null for legacy fallback. + */ + public static function link_generation_logs( int $post_id, ?string $run_id = null ): void { + global $wpdb; + $table = $wpdb->prefix . 'prautoblogger_generation_log'; + + if ( null !== $run_id && '' !== $run_id ) { + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery + $wpdb->query( + $wpdb->prepare( + "UPDATE {$table} SET post_id = %d WHERE post_id IS NULL AND run_id = %s", + $post_id, + $run_id + ) + ); + } else { + // Legacy fallback: timestamp-based linking for pre-migration log entries. + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery + $wpdb->query( + $wpdb->prepare( + "UPDATE {$table} SET post_id = %d WHERE post_id IS NULL AND created_at >= %s", + $post_id, + gmdate( 'Y-m-d H:i:s', time() - HOUR_IN_SECONDS ) + ) + ); + } + } + + /** + * Strip common LLM output artifacts from generated content. + * Removes markdown fences, HTML document wrappers, stray head-level tags. + * + * @param string $content Raw LLM output. + * @return string Cleaned HTML suitable for post_content. + */ + public static function sanitize_llm_content( string $content ): string { + $content = trim( $content ); + + // Strip markdown code fences: ```html ... ``` or ``` ... ``` + if ( preg_match( '/^```(?:\w+)?\s*\n(.*?)```\s*$/s', $content, $matches ) ) { + $content = trim( $matches[1] ); + } + + // Strip full HTML document wrappers — extract only
inner content. + if ( preg_match( '/]*>(.*)<\/body>/is', $content, $matches ) ) { + $content = trim( $matches[1] ); + } + + // Remove stray document-level tags. + $content = preg_replace( '/<\/?(html|head|body|meta|title|!DOCTYPE)[^>]*>/i', '', $content ); + $content = preg_replace( '/]*>/i', '', $content ); + $content = preg_replace( '/