cap->edit_posts ) ) { return new WP_Error( 'acsagen_forbidden', 'page' === $post_type ? __( 'You are not allowed to create pages on this site.', 'acs-article-generator' ) : __( 'You are not allowed to create posts on this site.', 'acs-article-generator' ) ); } $title = sanitize_text_field( (string) $article['title'] ); $description = self::clean_description( (string) $article['meta_description'] ); $slug = sanitize_title( (string) $article['slug'] ); $content = self::clean_content( (string) $article['content_html'] ); if ( '' === $title ) { return new WP_Error( 'acsagen_empty_title', __( 'The reply did not contain a usable title. Please try again.', 'acs-article-generator' ) ); } $postarr = array( 'post_title' => $title, 'post_content' => $content, 'post_excerpt' => $description, 'post_name' => $slug, 'post_status' => 'draft', 'post_type' => $post_type, 'post_author' => get_current_user_id(), ); if ( ! $skip_terms && ACSAGEN_Settings::get( 'draft_category', 1 ) ) { $categories = self::resolve_terms( isset( $article['categories'] ) ? (array) $article['categories'] : array(), 'category' ); if ( ! empty( $categories ) ) { $postarr['post_category'] = $categories; } } /** * Filters the draft just before it is inserted. * * @param array $postarr Arguments for wp_insert_post(). * @param array $article The decoded article data. */ $postarr = apply_filters( 'acsagen_draft_postarr', $postarr, $article ); // Keep the status out of anyone else's hands. A draft is a promise the // plugin makes to the site owner, not a default. $postarr['post_status'] = 'draft'; $post_id = wp_insert_post( $postarr, true ); if ( is_wp_error( $post_id ) ) { return $post_id; } if ( ! $skip_terms && ACSAGEN_Settings::get( 'draft_category', 1 ) ) { $tags = self::clean_term_names( isset( $article['tags'] ) ? (array) $article['tags'] : array() ); if ( ! empty( $tags ) ) { wp_set_post_terms( $post_id, $tags, 'post_tag', false ); } } if ( ACSAGEN_Settings::get( 'sync_seo_meta', 1 ) && '' !== $description ) { self::sync_seo_meta( $post_id, $title, $description ); } return array( 'post_id' => (int) $post_id, 'title' => (string) get_the_title( $post_id ), 'slug' => (string) get_post_field( 'post_name', $post_id ), 'description' => $description, 'edit_url' => (string) get_edit_post_link( $post_id, 'raw' ), ); } /** * Hand the SEO title and description to whichever SEO plugin is active. * * The description is also kept in the post excerpt, which is where * WordPress itself looks when no SEO plugin is installed. * * @param int $post_id Draft id. * @param string $title SEO title. * @param string $description Meta description. */ private static function sync_seo_meta( $post_id, $title, $description ) { // ACS SEO Plus. if ( class_exists( 'ACSSEO_Meta' ) ) { update_post_meta( $post_id, '_acsseo_title', $title ); update_post_meta( $post_id, '_acsseo_description', $description ); } // Yoast SEO. if ( defined( 'WPSEO_VERSION' ) ) { update_post_meta( $post_id, '_yoast_wpseo_title', $title ); update_post_meta( $post_id, '_yoast_wpseo_metadesc', $description ); } } /** * Trim a meta description to a sensible length. * * @param string $text Raw description. * @return string */ private static function clean_description( $text ) { $text = sanitize_text_field( wp_strip_all_tags( $text ) ); return mb_substr( $text, 0, 200 ); } /** * Strip anything from the article body that has no business in a post. * * The body is written by a model, so it is treated as untrusted input and * run through the same filter WordPress applies to post content from a * contributor, which keeps block comments but removes scripts, styles, * iframes, event handlers and javascript URLs. * * @param string $html Raw article body. * @return string */ private static function clean_content( $html ) { $allowed = wp_kses_allowed_html( 'post' ); // Block editor markup lives in HTML comments, which wp_kses keeps. $clean = wp_kses( $html, $allowed ); return trim( $clean ); } /** * Turn a list of term names into term ids, creating any that are missing. * * @param array $names Term names. * @param string $taxonomy Taxonomy name. * @return array Term ids. */ private static function resolve_terms( $names, $taxonomy ) { $ids = array(); foreach ( self::clean_term_names( $names ) as $name ) { $term = get_term_by( 'name', $name, $taxonomy ); if ( $term && ! is_wp_error( $term ) ) { $ids[] = (int) $term->term_id; continue; } if ( ! current_user_can( 'manage_categories' ) ) { continue; } $created = wp_insert_term( $name, $taxonomy ); if ( ! is_wp_error( $created ) && isset( $created['term_id'] ) ) { $ids[] = (int) $created['term_id']; } } return $ids; } /** * Clean a list of term names. * * @param array $names Raw names. * @return array */ private static function clean_term_names( $names ) { $clean = array(); foreach ( $names as $name ) { if ( ! is_string( $name ) ) { continue; } $name = sanitize_text_field( $name ); if ( '' === $name ) { continue; } $clean[] = mb_substr( $name, 0, 60 ); if ( count( $clean ) >= 10 ) { break; } } return array_values( array_unique( $clean ) ); } }