if (!defined('FCCG_PLUGIN_URL')) { define('FCCG_PLUGIN_URL', plugin_dir_url(__FILE__)); } /** * Optimierte Klasse für den Partner-Artikel Generator. * Erstellt vollständige Beiträge inkl. SEO-Daten, Bild und Kommentaren * mit nur einer einzigen, strukturierten KI-Anfrage. * * @package Family_Content_Composer_Gemini */ defined('ABSPATH') or die('Direct access not allowed!'); class FCCG_Amazon_Post_Generator { /** * Registers the form submission processing method with the WordPress `admin_post` hook. * Also registers the hook to load admin styles. */ public function register_hooks(): void { add_action('admin_post_fccg_create_amazon_post', [$this, 'handle_form_submission']); // Add the hook to load the CSS in the admin area add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_styles']); } /** * Enqueues the admin styles for the plugin. * Loads the 'amazon-styles.css' file, which includes the Lobster font. */ public function enqueue_admin_styles(): void { // Check if we are on the correct admin page. // Replace 'fccg-amazon-generator' with the actual slug of your admin page. // This prevents the CSS from loading unnecessarily on other admin pages. if (isset($_GET['page']) && $_GET['page'] === 'fccg-amazon-generator') { wp_enqueue_style( 'fccg-amazon-styles', // Unique handle for your stylesheet FCCG_PLUGIN_URL . 'admin/css/amazon-styles.css', // Using FCCG_PLUGIN_URL [], // Dependencies (empty array as there are none) filemtime(FCCG_PLUGIN_DIR . 'admin/css/amazon-styles.css') // Versioning based on file modification time ); } } /** * Processes the form submission for creating a post. */ public function handle_form_submission(): void { if (!current_user_can('publish_posts') || !check_admin_referer('fccg_create_amazon_post_nonce')) { wp_die('Sicherheitsüberprüfung fehlgeschlagen.'); } $form_data = $this->get_sanitized_form_data(); if (is_wp_error($form_data)) { $this->set_redirect_message($form_data->get_error_message(), 'error'); $this->redirect_back(); } FCCG_Helper::log_entry("Starte Partner-Artikel-Erstellung für: {$form_data['product_title']}", 'process'); $ai_content = $this->generate_ai_content($form_data); if (is_wp_error($ai_content)) { $error_message = 'Fehler bei der KI: ' . $ai_content->get_error_message() . '. Der Beitrag wurde als Entwurf mit Platzhaltern gespeichert.'; // Ensure the post status is 'draft' for the placeholder post $form_data['post_status'] = 'draft'; // Create a placeholder post if AI generation fails $new_post_id = $this->create_wordpress_post( [ 'title' => $form_data['product_title'] . ' (KI-Fehler)', 'content' => '', 'tags' => [], 'excerpt' => 'Beitrag konnte nicht von der KI generiert werden.', ], $form_data ); if (!is_wp_error($new_post_id)) { $this->process_post_extras($new_post_id, $form_data['image_url'], $form_data['product_title']); // Save original form data as post meta for retry update_post_meta($new_post_id, '_fccg_original_form_data', $form_data); $this->set_redirect_message($error_message . ' Entwurf ansehen.', 'error'); } else { $this->set_redirect_message('Fehler beim Speichern des Entwurfs: ' . $new_post_id->get_error_message(), 'error'); } $this->redirect_back(); return; } $new_post_id = $this->create_wordpress_post($ai_content, $form_data); if (is_wp_error($new_post_id)) { $this->set_redirect_message('Fehler beim Speichern des Beitrags: ' . $new_post_id->get_error_message(), 'error'); } else { FCCG_Helper::log_entry("Neuen Partner-Artikel erstellt (ID: {$new_post_id}).", 'success'); $this->process_post_extras($new_post_id, $form_data['image_url'], $ai_content['title']); // Save original form data as post meta update_post_meta($new_post_id, '_fccg_original_form_data', $form_data); $edit_link = get_edit_post_link($new_post_id, 'raw'); $view_link = get_permalink($new_post_id); $message = 'Neuer, vollständiger Beitrag wurde erfolgreich erstellt! Hier bearbeiten.'; $this->set_redirect_message($message, 'success', $view_link); } $this->redirect_back(); } /** * Safely retrieves and sanitizes form data. * Also considers data from the "retry" button. */ private function get_sanitized_form_data(): array|WP_Error { $data = [ 'partner_link' => isset($_POST['fccg_partner_link']) ? sanitize_text_field(wp_unslash($_POST['fccg_partner_link'])) : '', 'product_title' => isset($_POST['fccg_product_title']) ? sanitize_text_field(wp_unslash($_POST['fccg_product_title'])) : '', 'image_url' => isset($_POST['fccg_product_image_url']) ? esc_url_raw(wp_unslash($_POST['fccg_product_image_url'])) : '', 'about_info' => isset($_POST['fccg_product_description']) ? sanitize_text_field(wp_unslash($_POST['fccg_product_description'])) : '', 'promo_info' => isset($_POST['fccg_promo_info']) ? sanitize_text_field(wp_unslash($_POST['fccg_promo_info'])) : '', 'post_status' => isset($_POST['fccg_post_status']) ? sanitize_key($_POST['fccg_post_status']) : 'draft', 'schedule_date' => isset($_POST['fccg_schedule_date']) ? sanitize_text_field($_POST['fccg_schedule_date']) : '', 'schedule_time' => isset($_POST['fccg_schedule_time']) ? sanitize_text_field($_POST['fccg_schedule_time']) : '', 'post_id_to_retry' => isset($_POST['fccg_post_id_to_retry']) ? absint($_POST['fccg_post_id_to_retry']) : 0, // New: Post ID for retry ]; // If the request comes from a "retry" button (for existing drafts) if ($data['post_id_to_retry'] > 0) { $original_data = get_post_meta($data['post_id_to_retry'], '_fccg_original_form_data', true); if (is_array($original_data)) { // Merge/overwrite the current POST data with the stored original data $data = array_merge($data, $original_data); // Set the status to 'publish' for retry, unless it was originally scheduled if (!isset($original_data['post_status']) || $original_data['post_status'] !== 'schedule') { $data['post_status'] = 'publish'; // Default to publish on retry } } } if (empty($data['product_title']) || empty($data['partner_link'])) { return new WP_Error('missing_fields', 'Produktname und Partner-Link sind Pflichtfelder.'); } return $data; } /** * Generates AI content. */ private function generate_ai_content(array $data): array|WP_Error { $prompt = $this->build_structured_prompt($data); $api_client = new FCCG_API_Client(); $api_result = $api_client->call_gemini_api_for_json($prompt, 1800); if (is_wp_error($api_result)) { return $api_result; } if (!is_array($api_result) || !isset($api_result['blogContent'])) { return new WP_Error('invalid_array_response', 'Die KI hat eine ungültige oder leere Antwortstruktur zurückgegeben.'); } // Add at least 5 '#' characters at the beginning of the excerpt $excerpt = $api_result['seoExcerpt'] ?? ''; $api_result['seoExcerpt'] = '##### ' . $excerpt; return [ 'title' => $api_result['postTitle'] ?? $data['product_title'], 'content' => $api_result['blogContent'] ?? '', 'tags' => $api_result['tags'] ?? [], 'excerpt' => $api_result['seoExcerpt'], // Use the modified excerpt ]; } /** * Creates or updates a post in the WordPress database. * If post_id_to_retry is set, an existing post will be updated. */ private function create_wordpress_post(array $ai_content, array $form_data): int|WP_Error { $post_id_to_update = $form_data['post_id_to_retry'] ?? 0; // Get the ID of the post to update $post_data = [ 'post_title' => $ai_content['title'], 'post_content' => wp_kses_post(trim($ai_content['content'])), 'post_author' => get_option('fccg_post_author_id', 1), 'post_category'=> [FCCG_Helper::get_or_create_category_id('amazon', 'Amazon')], // Changed to 'Amazon' category 'tags_input' => is_array($ai_content['tags']) ? implode(', ', $ai_content['tags']) : '', 'post_excerpt' => sanitize_textarea_field(trim($ai_content['excerpt'])), 'ID' => $post_id_to_update, // Add ID if updating ]; if ($form_data['post_status'] === 'schedule' && !empty($form_data['schedule_date'])) { $time = !empty($form_data['schedule_time']) ? $form_data['schedule_time'] . ':00' : '00:00:00'; $post_date_string = $form_data['schedule_date'] . ' ' . $time; $post_data['post_date'] = $post_date_string; $post_data['post_date_gmt'] = get_gmt_from_date($post_date_string); $post_data['post_status'] = 'future'; } else { $post_data['post_status'] = in_array($form_data['post_status'], ['publish', 'draft']) ? $form_data['post_status'] : 'draft'; } if ($post_id_to_update > 0) { // Update an existing post return wp_update_post(wp_slash($post_data), true); } else { // Insert a new post return wp_insert_post(wp_slash($post_data), true); } } /** * Processes additional tasks after post creation/update. */ private function process_post_extras(int $post_id, string $image_url, string $post_title): void { if (!empty($image_url)) { $this->sideload_and_set_thumbnail($image_url, $post_id, $post_title); } if (class_exists('FCCG_Comment_Generator')) { (new FCCG_Comment_Generator())->process_single_post($post_id); } } /** * Builds the structured prompt that requests a JSON response from the AI. * The sales text is psychologically appealing, written by a woman, * with many icons, testimonials, multiple colored buttons for partner links * and a strong call-to-action. */ private function build_structured_prompt(array $data): string { $prompt = "TASK: Erstelle einen extrem psychologisch überzeugenden, emotionalen und verkaufsstarken Blog-Beitrag für ein Produkt im Stil einer euphorischen Schlager-Album-Rezension. Der Text soll aus der Perspektive einer begeisterten Frau geschrieben sein, die das Produkt liebt und es aus tiefstem Herzen empfiehlt.\n"; $prompt .= "PRODUCT NAME: {$data['product_title']}\n"; $prompt .= "PARTNER LINK (STANDARD EDITION): {$data['partner_link']}\n"; if (!empty($data['promo_info'])) { $prompt .= "PARTNER LINK (DELUXE FANBOX): {$data['promo_info']}\n"; } if (!empty($data['about_info'])) { $prompt .= "PARTNER LINK (VINYL EDITION): {$data['about_info']}\n"; } $prompt .= "\nANFORDERUNGEN:\n"; $prompt .= "1. Gib deine Antwort AUSSCHLIESSLICH als gültiges JSON-Objekt zurück.\n"; $prompt .= "2. Das JSON-Objekt MUSS die folgenden Schlüssel enthalten: 'postTitle', 'blogContent', 'tags', 'seoExcerpt'.\n"; $prompt .= "3. Der 'blogContent' MUSS mindestens 2500 Zeichen lang sein (reiner Text, ohne HTML-Tags).\n"; $prompt .= "4. Der gesamte Text MUSS emotional, begeisternd und höchst überzeugend sein, als wäre er von einer begeisterten Frau geschrieben worden.\n"; $prompt .= "5. Verwende IM GESAMTEN TEXT reichlich passende, emotional verstärkende Emojis (z.B. ✨❤️🎶💃💿🔥🚀⭐💖🎉🤩) an verschiedenen Stellen, um die Begeisterung zu unterstreichen.\n"; $prompt .= "6. **INTEGRIERE Partnerlinks mehrfach (mindestens 5-7 Mal) im gesamten 'blogContent'.** Formatiere diese Links als ansprechende HTML-Buttons mit verschiedenen Farben und Schriften, die zu einer Schlager-Ästhetik passen (z.B. knallige Farben, serifenlose oder verspielte Schriftarten, etwas größer). Jeder Button muss auf einen der oben bereitgestellten Partnerlinks (`PARTNER LINK (STANDARD EDITION)`, `PARTNER LINK (DELUXE FANBOX)`, `PARTNER LINK (VINYL EDITION)`) verweisen. Nutze verschiedene Button-Texte, die zum sofortigen Kauf anregen. Beispiele für HTML-Buttons (KI muss diese Formate beibehalten und die Platzhalter ersetzen): * `❤️ Jetzt die STANDARD EDITION sichern! 🎉` * `🌟 Hol dir die DELUXE FANBOX! 🤩` * `💿 VINYL-TRAUM JETZT bestelllen! 🎶` Die KI muss die Platzhalter `[PARTNERLINK_STANDARD]`, `[PARTNERLINK_DELUXE]`, `[PARTNERLINK_VINYL]` durch die tatsächlich übergebenen URLs ersetzen.\n"; $prompt .= "7. Fülle den Abschnitt '