]*>.*?#is', '', $html ); $html = preg_replace( '//s', '', $html ); // Strip even unregistered shortcodes. Never render source-site content filters. $html = preg_replace( '/\[\/?[a-zA-Z_][^\]\r\n]*\]/', '', $html ); $tags = array_fill_keys( [ 'p', 'br', 'hr', 'strong', 'b', 'em', 'i', 's', 'blockquote', 'pre', 'code', 'ul', 'ol', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'table', 'thead', 'tbody', 'tr', 'th', 'td', 'figure', 'figcaption' ], [] ); return wp_kses( $html, $tags, [] ); } public static function build( array $ids ): array { if ( count( $ids ) > self::MAX_POSTS ) { throw new \RuntimeException( __( 'Choose at most 30 posts or pages.', 'smooth-sandbox' ) ); } $posts = []; foreach ( array_unique( $ids ) as $id ) { if ( ! is_scalar( $id ) || ! preg_match( '/^[1-9][0-9]*$/D', (string) $id ) ) { throw new \RuntimeException( __( 'Invalid content selection.', 'smooth-sandbox' ) ); } $post = get_post( (int) $id ); if ( ! $post || ! in_array( $post->post_type, [ 'post', 'page' ], true ) || 'publish' !== $post->post_status || '' !== $post->post_password || ! current_user_can( 'edit_post', $post->ID ) ) { throw new \RuntimeException( __( 'A selected item is no longer public or you cannot export it. Review the selection again.', 'smooth-sandbox' ) ); } $posts[] = [ 'type' => $post->post_type, 'title' => sanitize_text_field( $post->post_title ), 'content' => self::clean( $post->post_content ), 'excerpt' => self::clean( $post->post_excerpt ), ]; } $payload = [ 'protocol' => 1, 'name' => 'Smooth Sandbox', 'posts' => $posts ]; $json = wp_json_encode( $payload ); if ( false === $json || strlen( $json ) > self::MAX_BYTES ) { throw new \RuntimeException( __( 'The selected content exceeds 512 KB. Choose fewer items.', 'smooth-sandbox' ) ); } return $payload; } }