' . \esc_html( $error_message ) . '
'; \printf( /* translators: 1: php.ini, 2: post_max_size, 3: upload_max_filesize */ \esc_html__( 'File is empty. Please upload something more substantial. This error could also be caused by uploads being disabled in your %1$s file or by %2$s being defined as smaller than %3$s in %1$s.', 'activitypub' ), 'php.ini', 'post_max_size', 'upload_max_filesize' ); echo '

'; return false; } $file_info = \wp_check_filetype( \sanitize_file_name( $_FILES['import']['name'] ), array( 'json' => 'application/json' ) ); if ( 'application/json' !== $file_info['type'] ) { \printf( '

%s
%s

', \esc_html( $error_message ), \esc_html__( 'The uploaded file must be a JSON file. Please try again with the correct file format.', 'activitypub' ) ); return false; } $overrides = array( 'test_form' => false, 'test_type' => false, ); $upload = \wp_handle_upload( $_FILES['import'], $overrides ); if ( isset( $upload['error'] ) ) { \printf( '

%s
%s

', \esc_html( $error_message ), \esc_html( $upload['error'] ) ); return false; } // Construct the attachment array. $attachment = array( 'post_title' => \wp_basename( $upload['file'] ), 'post_content' => $upload['url'], 'post_mime_type' => $upload['type'], 'guid' => $upload['url'], 'context' => 'import', 'post_status' => 'private', ); // Save the data. self::$import_id = \wp_insert_attachment( $attachment, $upload['file'] ); // Schedule a cleanup for one day from now in case of failed import or missing wp_import_cleanup() call. \wp_schedule_single_event( time() + DAY_IN_SECONDS, 'importer_scheduled_cleanup', array( self::$import_id ) ); return true; } /** * Handle URL import. */ public static function handle_url_import() { $error_message = \__( 'Sorry, there has been an error.', 'activitypub' ); // phpcs:ignore WordPress.Security.NonceVerification.Missing $url = \sanitize_url( \wp_unslash( $_POST['import_url'] ?? '' ) ); if ( empty( $url ) ) { echo '

' . \esc_html( $error_message ) . '
'; echo \esc_html__( 'Please provide a valid URL.', 'activitypub' ) . '

'; return false; } // Validate URL format. if ( ! \filter_var( $url, FILTER_VALIDATE_URL ) ) { \printf( '

%s
%s

', \esc_html( $error_message ), \esc_html__( 'The provided URL is not valid.', 'activitypub' ) ); return false; } // Fetch the URL content. $response = \wp_remote_get( $url, array( 'timeout' => 30, 'redirection' => 5, 'headers' => array( 'Accept' => 'application/activity+json', ), ) ); if ( \is_wp_error( $response ) ) { \printf( '

%s
%s

', \esc_html( $error_message ), \esc_html( $response->get_error_message() ) ); return false; } $response_code = \wp_remote_retrieve_response_code( $response ); if ( 200 !== $response_code ) { \printf( '

%s
%s

', \esc_html( $error_message ), /* translators: %d: HTTP response code */ \esc_html( \sprintf( \__( 'Failed to fetch URL. HTTP response code: %d', 'activitypub' ), $response_code ) ) ); return false; } $body = \wp_remote_retrieve_body( $response ); if ( empty( $body ) ) { \printf( '

%s
%s

', \esc_html( $error_message ), \esc_html__( 'The URL returned empty content.', 'activitypub' ) ); return false; } // Validate JSON format. $json_data = \json_decode( $body, true ); if ( null === $json_data ) { \printf( '

%s
%s

', \esc_html( $error_message ), \esc_html__( 'The URL does not contain valid JSON data.', 'activitypub' ) ); return false; } // Create a temporary file to store the JSON content. $upload_dir = \wp_upload_dir(); $base_filename = 'starter-kit.json'; $unique_filename = \wp_unique_filename( $upload_dir['path'], $base_filename ); $temp_file = \trailingslashit( $upload_dir['path'] ) . $unique_filename; if ( ! \WP_Filesystem() ) { \printf( '

%s
%s

', \esc_html( $error_message ), \esc_html__( 'Failed to initialize the WordPress filesystem.', 'activitypub' ) ); return false; } global $wp_filesystem; if ( ! $wp_filesystem || ! is_a( $wp_filesystem, 'WP_Filesystem_Base' ) ) { \printf( '

%s
%s

', \esc_html( $error_message ), \esc_html__( 'Failed to initialize the WordPress filesystem.', 'activitypub' ) ); return false; } if ( ! $wp_filesystem->put_contents( $temp_file, $body, FS_CHMOD_FILE ) ) { \printf( '

%s
%s

', \esc_html( $error_message ), \esc_html__( 'Failed to save the downloaded content.', 'activitypub' ) ); return false; } // Construct the attachment array. $attachment = array( // phpcs:ignore 'post_title' => \sanitize_file_name( \basename( \wp_parse_url( $url, PHP_URL_PATH ) ) ) ?: 'starter-kit.json', 'post_content' => $url, 'post_mime_type' => 'application/json', 'guid' => $url, 'context' => 'import', 'post_status' => 'private', ); // Save the data. self::$import_id = \wp_insert_attachment( $attachment, $temp_file ); // Check if the attachment was inserted successfully. if ( \is_wp_error( self::$import_id ) || ! self::$import_id ) { \printf( '

%s
%s

', \esc_html( $error_message ), \esc_html__( 'Failed to insert attachment.', 'activitypub' ) ); return false; } // Schedule a cleanup for one day from now in case of failed import or missing wp_import_cleanup() call. \wp_schedule_single_event( \time() + DAY_IN_SECONDS, 'importer_scheduled_cleanup', array( self::$import_id ) ); return true; } /** * Import options. */ public static function import_options() { self::setup_blog_user_filter(); $actors = self::get_actor_list(); if ( \is_wp_error( $actors ) ) { self::render_error( $actors ); return; } self::render_import_form( $actors ); self::cleanup_blog_user_filter(); } /** * Setup blog user filter for dropdown. */ private static function setup_blog_user_filter() { if ( is_user_type_disabled( 'blog' ) ) { return; } self::$blog_user_filter_callback = static function ( $users ) { return \preg_replace( '/<\/select>/', '', $users ); }; \add_filter( 'wp_dropdown_users', self::$blog_user_filter_callback ); self::$blog_user_filter_added = true; } /** * Cleanup blog user filter. */ private static function cleanup_blog_user_filter() { if ( self::$blog_user_filter_callback && self::$blog_user_filter_added ) { \remove_filter( 'wp_dropdown_users', self::$blog_user_filter_callback ); self::$blog_user_filter_callback = null; } self::$blog_user_filter_added = false; } /** * Render error message. * * @param \WP_Error $error The error to render. */ private static function render_error( $error ) { \printf( '

%s
%s

', \esc_html__( 'Sorry, there has been an error.', 'activitypub' ), \esc_html( $error->get_error_message() ) ); } /** * Render the import form. * * @param array $actors The actors to render. */ private static function render_import_form( $actors ) { ?>

' . \esc_html( $name ) . ''; if ( ! empty( self::$starter_kit['image']['url'] ) ) { \printf( '%s', \esc_url( self::$starter_kit['image']['url'] ), \esc_attr( self::$starter_kit['image']['summary'] ?? '' ) ); } if ( ! empty( self::$starter_kit['summary'] ) ) { echo '

' . \esc_html( self::$starter_kit['summary'] ) . '

'; } if ( ! empty( self::$starter_kit['attributedTo'] ) ) { echo \wp_kses_post( \sprintf( 'Created by %1$s', \esc_url( self::$starter_kit['attributedTo'] ) ) ); } } /** * Render author selection. */ private static function render_author_selection() { ?>

'author', 'id' => 'author', 'show' => 'display_name_with_login', 'selected' => \get_current_user_id(), 'capability' => 'activitypub', ) ); ?>

%s
%s

', \esc_html( $error_message ), \esc_html( $result->get_error_message() ) ); } else { \printf( '

%s

', \esc_html__( 'All done.', 'activitypub' ) ); } /** * Fires when the Starter Kit import ends. */ \do_action( 'import_end' ); } /** * Process posts. * * @return true|\WP_Error True on success, WP_Error on failure. */ public static function follow() { $skipped = 0; $followed = 0; $items = self::$actor_list; foreach ( $items as $actor_id ) { $actor_id = object_to_uri( $actor_id ); $actor_id = \ltrim( $actor_id, '@' ); if ( ! filter_var( $actor_id, FILTER_VALIDATE_URL ) && ! filter_var( $actor_id, FILTER_VALIDATE_EMAIL ) ) { ++$skipped; continue; } $result = follow( $actor_id, self::$author ); if ( \is_wp_error( $result ) ) { /* translators: %s: Account ID */ \printf( '

' . \esc_html__( '✗ %s', 'activitypub' ) . '

', \esc_html( $actor_id ) ); ++$skipped; } else { /* translators: %s: Account ID */ \printf( '

' . \esc_html__( '✓ %s', 'activitypub' ) . '

', \esc_html( $actor_id ) ); ++$followed; } } echo '
'; /* translators: %d: Number of followed actors */ \printf( '

%s

', \esc_html( \sprintf( \_n( 'Followed %s Actor.', 'Followed %s Actors.', $followed, 'activitypub' ), \number_format_i18n( $followed ) ) ) ); /* translators: %d: Number of skipped items */ \printf( '

%s

', \esc_html( \sprintf( \_n( 'Skipped %s Item.', 'Skipped %s Items.', $skipped, 'activitypub' ), \number_format_i18n( $skipped ) ) ) ); return true; } /** * Intro. */ public static function greet() { echo '
'; echo '

' . \esc_html__( 'Starter Kits use the ActivityPub protocol with custom extensions to automate tasks such as following accounts, blocking unwanted content, and applying default configurations. The importer will automatically follow every user listed in the kit, helping users connect right away. Support for additional actions and features will be added over time.', 'activitypub' ) . '

'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended $url = isset( $_GET['url'] ) ? \sanitize_text_field( \wp_unslash( $_GET['url'] ) ) : ''; if ( empty( $url ) ) { // File upload option. \printf( '

%s

', \esc_html__( 'Option 1: Upload a File', 'activitypub' ) ); \wp_import_upload_form( 'admin.php?import=starter-kit&step=1' ); // URL import option. \printf( '

%s

', \esc_html__( 'Option 2: Import from URL', 'activitypub' ) ); } else { // URL import option. \printf( '

%s

', \esc_html__( 'Import from URL', 'activitypub' ) ); } ?>

'; echo '

' . \esc_html__( 'Import a Fediverse Starter Kit (Beta)', 'activitypub' ) . '

'; } /** * Footer. */ public static function footer() { echo ''; } /** * Get actor list. */ private static function get_actor_list() { $file = \get_attached_file( self::$import_id ); \WP_Filesystem(); global $wp_filesystem; $file_contents = $wp_filesystem->get_contents( $file ); if ( false === $file_contents ) { return new \WP_Error( 'file_not_found', \esc_html__( 'Could not read the uploaded file.', 'activitypub' ) ); } self::$starter_kit = \json_decode( $file_contents, true ); if ( null === self::$starter_kit ) { return new \WP_Error( 'invalid_json', \esc_html__( 'Invalid JSON format in the uploaded file.', 'activitypub' ) ); } $actors = self::$starter_kit['items'] ?? self::$starter_kit['orderedItems'] ?? array(); // Limit list to 150 actors. // TODO: Make this configurable. $actors = \array_slice( $actors, 0, 150 ); if ( ! $actors ) { return new \WP_Error( 'empty_actor_list', \esc_html__( 'The uploaded file does not contain any actors.', 'activitypub' ) ); } return $actors; } }