true, 'single' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', ) ); \register_post_meta( $post_type, 'activitypub_content_visibility', array( 'type' => 'string', 'single' => true, 'show_in_rest' => true, 'sanitize_callback' => function ( $value ) { $schema = array( 'type' => 'string', 'enum' => array( ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC, ACTIVITYPUB_CONTENT_VISIBILITY_QUIET_PUBLIC, ACTIVITYPUB_CONTENT_VISIBILITY_PRIVATE, ACTIVITYPUB_CONTENT_VISIBILITY_LOCAL ), 'default' => ACTIVITYPUB_CONTENT_VISIBILITY_PUBLIC, ); if ( is_wp_error( rest_validate_enum( $value, $schema, '' ) ) ) { return $schema['default']; } return $value; }, ) ); \register_post_meta( $post_type, 'activitypub_max_image_attachments', array( 'type' => 'integer', 'single' => true, 'show_in_rest' => true, 'default' => \get_option( 'activitypub_max_image_attachments', ACTIVITYPUB_MAX_IMAGE_ATTACHMENTS ), 'sanitize_callback' => 'absint', ) ); } } /** * Enqueue the block editor assets. */ public static function enqueue_editor_assets() { $data = array( 'namespace' => ACTIVITYPUB_REST_NAMESPACE, 'defaultAvatarUrl' => ACTIVITYPUB_PLUGIN_URL . 'assets/img/mp.jpg', 'enabled' => array( 'blog' => ! is_user_type_disabled( 'blog' ), 'users' => ! is_user_type_disabled( 'user' ), ), ); wp_localize_script( 'wp-editor', '_activityPubOptions', $data ); // Check for our supported post types. $current_screen = \get_current_screen(); $ap_post_types = \get_post_types_by_support( 'activitypub' ); if ( ! $current_screen || ! in_array( $current_screen->post_type, $ap_post_types, true ) ) { return; } $asset_data = include ACTIVITYPUB_PLUGIN_DIR . 'build/editor-plugin/plugin.asset.php'; $plugin_url = plugins_url( 'build/editor-plugin/plugin.js', ACTIVITYPUB_PLUGIN_FILE ); wp_enqueue_script( 'activitypub-block-editor', $plugin_url, $asset_data['dependencies'], $asset_data['version'], true ); } /** * Enqueue the reply handle script if the in_reply_to GET param is set. */ public static function handle_in_reply_to_get_param() { // Only load the script if the in_reply_to GET param is set, action happens there, not here. // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( ! isset( $_GET['in_reply_to'] ) ) { return; } $asset_data = include ACTIVITYPUB_PLUGIN_DIR . 'build/reply-intent/plugin.asset.php'; $plugin_url = plugins_url( 'build/reply-intent/plugin.js', ACTIVITYPUB_PLUGIN_FILE ); wp_enqueue_script( 'activitypub-reply-intent', $plugin_url, $asset_data['dependencies'], $asset_data['version'], true ); } /** * Register the blocks. */ public static function register_blocks() { \register_block_type_from_metadata( ACTIVITYPUB_PLUGIN_DIR . '/build/follow-me' ); \register_block_type_from_metadata( ACTIVITYPUB_PLUGIN_DIR . '/build/followers' ); \register_block_type_from_metadata( ACTIVITYPUB_PLUGIN_DIR . '/build/reactions' ); \register_block_type_from_metadata( ACTIVITYPUB_PLUGIN_DIR . '/build/reply', array( 'render_callback' => array( self::class, 'render_reply_block' ), ) ); } /** * Register REST fields needed for blocks. */ public static function register_rest_fields() { // Register the post_count field for Follow Me block. register_rest_field( 'user', 'post_count', array( /** * Get the number of published posts. * * @param array $response Prepared response array. * @param string $field_name The field name. * @param \WP_REST_Request $request The request object. * @return int The number of published posts. */ 'get_callback' => function ( $response, $field_name, $request ) { return (int) count_user_posts( $request->get_param( 'id' ), 'post', true ); }, 'schema' => array( 'description' => 'Number of published posts', 'type' => 'integer', 'context' => array( 'activitypub' ), ), ) ); } /** * Get the user ID from a user string. * * @param string $user_string The user string. Can be a user ID, 'blog', or 'inherit'. * @return int|null The user ID, or null if the 'inherit' string is not supported in this context. */ public static function get_user_id( $user_string ) { if ( is_numeric( $user_string ) ) { return absint( $user_string ); } // If the user string is 'blog', return the Blog User ID. if ( 'blog' === $user_string ) { return Actors::BLOG_USER_ID; } // The only other value should be 'inherit', which means to use the query context to determine the User. if ( 'inherit' !== $user_string ) { return null; } // For a homepage/front page, if the Blog User is active, use it. if ( ( is_front_page() || is_home() ) && ! is_user_type_disabled( 'blog' ) ) { return Actors::BLOG_USER_ID; } // If we're in a loop, use the post author. $author_id = get_the_author_meta( 'ID' ); if ( $author_id ) { return $author_id; } // For other pages, the queried object will clue us in. $queried_object = get_queried_object(); if ( ! $queried_object ) { return null; } // If we're on a user archive page, use that user's ID. if ( is_a( $queried_object, 'WP_User' ) ) { return $queried_object->ID; } // For a single post, use the post author's ID. if ( is_a( $queried_object, 'WP_Post' ) ) { return get_the_author_meta( 'ID' ); } // We won't properly account for some conditions, like tag archives. return null; } /** * Render the reply block. * * @param array $attrs The block attributes. * * @return string The HTML to render. */ public static function render_reply_block( $attrs ) { // Return early if no URL is provided. if ( empty( $attrs['url'] ) ) { return null; } $show_embed = isset( $attrs['embedPost'] ) && $attrs['embedPost']; $wrapper_attrs = get_block_wrapper_attributes( array( 'aria-label' => __( 'Reply', 'activitypub' ), 'class' => 'activitypub-reply-block', 'data-in-reply-to' => $attrs['url'], ) ); $html = '
', esc_url( $attrs['url'] ), esc_attr__( 'This post is a response to the referenced content.', 'activitypub' ), // translators: %s is the URL of the post being replied to. sprintf( __( '↬%s', 'activitypub' ), \str_replace( array( 'https://', 'http://' ), '', esc_url( $attrs['url'] ) ) ) ); } $html .= '