get_param( 'title' ) ); $content = wp_kses_post( (string) $request->get_param( 'content' ) ); $slug = sanitize_title( (string) $request->get_param( 'slug' ) ); $excerpt = sanitize_textarea_field( (string) $request->get_param( 'meta_description' ) ); $article_id = sanitize_text_field( (string) $request->get_param( 'admetric_article_id' ) ); $image_url = (string) $request->get_param( 'featured_image_url' ); $seo_title = sanitize_text_field( (string) $request->get_param( 'seo_title' ) ); $focus_kw = sanitize_text_field( (string) $request->get_param( 'focus_keyword' ) ); if ( '' === $title || '' === trim( wp_strip_all_tags( $content ) ) ) { return new WP_Error( 'admetric_invalid_article', __( 'Article title and content are required.', 'admetric' ), array( 'status' => 400 ) ); } $status = $this->resolve_status( (string) $request->get_param( 'status' ) ); $postarr = array( 'post_title' => $title, 'post_content' => $content, 'post_excerpt' => $excerpt, 'post_status' => $status, 'post_type' => 'post', 'post_author' => AdMetric_Settings::default_author(), ); if ( '' !== $slug ) { $postarr['post_name'] = $slug; } $categories = $this->resolve_categories( $request->get_param( 'categories' ) ); if ( ! empty( $categories ) ) { $postarr['post_category'] = $categories; } elseif ( AdMetric_Settings::default_category() > 0 ) { $postarr['post_category'] = array( AdMetric_Settings::default_category() ); } $tags = $this->resolve_tags( $request->get_param( 'tags' ) ); if ( ! empty( $tags ) ) { $postarr['tags_input'] = $tags; } // Idempotency: same AdMetric article id updates the existing post. $existing_id = $article_id ? $this->find_by_article_id( $article_id ) : 0; $updated = false; if ( $existing_id ) { $postarr['ID'] = $existing_id; $post_id = wp_update_post( wp_slash( $postarr ), true ); $updated = true; } else { $post_id = wp_insert_post( wp_slash( $postarr ), true ); } if ( is_wp_error( $post_id ) ) { $post_id->add_data( array( 'status' => 500 ) ); return $post_id; } if ( $article_id ) { update_post_meta( $post_id, self::META_ARTICLE_ID, $article_id ); } $this->apply_faq( $post_id, $request->get_param( 'faq' ) ); $this->apply_seo_meta( $post_id, $seo_title, $excerpt, $focus_kw ); $image_result = null; if ( '' !== $image_url ) { $image_result = $this->sideload_featured_image( $post_id, $image_url, $title ); } return array( 'ok' => true, 'post_id' => (int) $post_id, 'post_url' => get_permalink( $post_id ), 'post_status' => get_post_status( $post_id ), 'updated' => $updated, 'featured_image' => is_wp_error( $image_result ) ? 'failed' : ( null === $image_result ? 'none' : 'ok' ), ); } /** * Effective post status: site owner's setting wins over the request. * * @param string $requested Requested status. * @return string 'publish' or 'draft'. */ private function resolve_status( $requested ) { $default = AdMetric_Settings::default_post_status(); if ( 'draft' === $default ) { // Site owner chose moderation: everything lands as draft. return 'draft'; } return ( 'draft' === $requested ) ? 'draft' : 'publish'; } /** * Find a previously published post by AdMetric article id. * * @param string $article_id AdMetric article id. * @return int Post ID or 0. */ private function find_by_article_id( $article_id ) { $found = get_posts( array( 'post_type' => 'post', 'post_status' => 'any', 'posts_per_page' => 1, 'fields' => 'ids', 'meta_key' => self::META_ARTICLE_ID, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key 'meta_value' => $article_id, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value ) ); return $found ? (int) $found[0] : 0; } /** * Normalize categories parameter into an array of existing term IDs. * * @param mixed $raw Raw request value. * @return int[] */ private function resolve_categories( $raw ) { if ( ! is_array( $raw ) ) { return array(); } $ids = array(); foreach ( $raw as $item ) { if ( is_numeric( $item ) ) { $term = get_term( (int) $item, 'category' ); } else { $term = get_term_by( 'name', sanitize_text_field( (string) $item ), 'category' ); } if ( $term instanceof WP_Term ) { $ids[] = (int) $term->term_id; } } return array_values( array_unique( $ids ) ); } /** * Normalize tags parameter into an array of tag names. * * @param mixed $raw Raw request value. * @return string[] */ private function resolve_tags( $raw ) { if ( ! is_array( $raw ) ) { return array(); } $tags = array(); foreach ( $raw as $item ) { $tag = sanitize_text_field( (string) $item ); if ( '' !== $tag ) { $tags[] = $tag; } } return $tags; } /** * Store sanitized FAQ entries as post meta (rendered as JSON-LD on the front end). * * @param int $post_id Post ID. * @param mixed $raw Raw faq request value. */ private function apply_faq( $post_id, $raw ) { if ( ! is_array( $raw ) ) { delete_post_meta( $post_id, self::META_FAQ ); return; } $faq = array(); foreach ( $raw as $item ) { if ( ! is_array( $item ) ) { continue; } $question = sanitize_text_field( (string) ( $item['question'] ?? '' ) ); $answer = sanitize_textarea_field( (string) ( $item['answer'] ?? '' ) ); if ( '' !== $question && '' !== $answer ) { $faq[] = array( 'question' => $question, 'answer' => $answer, ); } } if ( empty( $faq ) ) { delete_post_meta( $post_id, self::META_FAQ ); return; } update_post_meta( $post_id, self::META_FAQ, $faq ); } /** * Fill SEO plugin meta fields when Yoast SEO or Rank Math is active. * * @param int $post_id Post ID. * @param string $seo_title SEO title. * @param string $seo_desc Meta description. * @param string $focus_kw Focus keyword. */ private function apply_seo_meta( $post_id, $seo_title, $seo_desc, $focus_kw ) { if ( defined( 'WPSEO_VERSION' ) ) { if ( '' !== $seo_title ) { update_post_meta( $post_id, '_yoast_wpseo_title', $seo_title ); } if ( '' !== $seo_desc ) { update_post_meta( $post_id, '_yoast_wpseo_metadesc', $seo_desc ); } if ( '' !== $focus_kw ) { update_post_meta( $post_id, '_yoast_wpseo_focuskw', $focus_kw ); } } if ( class_exists( 'RankMath' ) ) { if ( '' !== $seo_title ) { update_post_meta( $post_id, 'rank_math_title', $seo_title ); } if ( '' !== $seo_desc ) { update_post_meta( $post_id, 'rank_math_description', $seo_desc ); } if ( '' !== $focus_kw ) { update_post_meta( $post_id, 'rank_math_focus_keyword', $focus_kw ); } } } /** * Download the featured image and attach it to the post. * * @param int $post_id Post ID. * @param string $url Image URL from the publish payload. * @param string $title Image title/alt. * @return true|WP_Error|null True on success, WP_Error on failure, null when skipped. */ private function sideload_featured_image( $post_id, $url, $title ) { $url = esc_url_raw( $url ); if ( '' === $url || ! wp_http_validate_url( $url ) ) { return new WP_Error( 'admetric_bad_image_url', __( 'Invalid featured image URL.', 'admetric' ) ); } require_once ABSPATH . 'wp-admin/includes/media.php'; require_once ABSPATH . 'wp-admin/includes/file.php'; require_once ABSPATH . 'wp-admin/includes/image.php'; $attachment_id = media_sideload_image( $url, $post_id, $title, 'id' ); if ( is_wp_error( $attachment_id ) ) { return $attachment_id; } set_post_thumbnail( $post_id, (int) $attachment_id ); update_post_meta( (int) $attachment_id, '_wp_attachment_image_alt', sanitize_text_field( $title ) ); return true; } }