true, 'type' => 'string', 'default' => '', 'show_in_rest' => array( 'schema' => array( 'type' => 'string', 'pattern' => Thread_Uri::PATTERN, ), ), 'auth_callback' => array( __CLASS__, 'can_edit' ), 'sanitize_callback' => array( __CLASS__, 'sanitize' ), ) ); add_action( "save_post_{$type}", array( __CLASS__, 'save_metabox' ), 10, 2 ); } } /** * Adds the classic editor metabox to active post types. Hidden in the * block editor, which has its own panel. * * @param string $post_type Post type of the screen. */ public static function add_metabox( $post_type ) { if ( ! Settings::is_active_type( $post_type ) ) { return; } add_meta_box( 'atproto-comments-thread', __( 'AT Protocol comments', 'parolas' ), array( __CLASS__, 'render_metabox' ), $post_type, 'side', 'default', array( '__back_compat_meta_box' => true ) ); } /** * Renders the metabox. * * @param \WP_Post $post Post. */ public static function render_metabox( $post ) { wp_nonce_field( 'atproto_comments_save_thread', self::NONCE ); $value = get_post_meta( $post->ID, self::KEY, true ); ?>
post_type || ! Settings::is_active_type( $screen->post_type ) ) { return; } $asset_file = ATPROTO_COMMENTS_DIR . 'build/editor.asset.php'; if ( ! is_readable( $asset_file ) ) { return; } $asset = require $asset_file; wp_enqueue_script( 'atproto-comments-editor', ATPROTO_COMMENTS_URL . 'build/editor.js', $asset['dependencies'], $asset['version'], true ); wp_set_script_translations( 'atproto-comments-editor', 'parolas' ); $post = get_post(); if ( $post ) { wp_add_inline_script( 'atproto-comments-editor', 'window.atprotoCommentsEditor = ' . wp_json_encode( self::editor_data( $post ) ) . ';', 'before' ); } } /** * Data for the block editor panel. Never includes the account identifier * or the password. * * `accountDid` is the DID the account last resolved to ('' if never): it is * public, unlike the identifier and the password. * * @param \WP_Post $post Post being edited. * @return array{accountReady: bool, accountDid: string, canPublish: bool, canDelete: bool, settingsUrl: string|null, appview: string, postUrlTemplate: string} */ public static function editor_data( $post ) { return array( 'accountReady' => Account::is_configured(), 'accountDid' => Account::get()['did'], 'canPublish' => Anchor_Post::user_can_publish( $post->ID ), 'canDelete' => Anchor_Post::user_can_delete( $post->ID ), 'settingsUrl' => current_user_can( 'manage_options' ) ? admin_url( 'options-general.php?page=' . Settings::PAGE ) : null, 'appview' => Settings::get()['appview'], 'postUrlTemplate' => self::post_url_template( $post ), ); } /** * URL template to open the linked post in an app, with `{did}` and * `{rkey}` placeholders. * * @param \WP_Post $post Post being edited. * @return string */ private static function post_url_template( $post ) { $default = 'https://bsky.app/profile/{did}/post/{rkey}'; /** * URL template to open the linked post from the editor panel. * * @param string $template https URL with `{did}` and `{rkey}`. Default * 'https://bsky.app/profile/{did}/post/{rkey}'. * @param \WP_Post $post Post being edited. */ $template = apply_filters( 'atproto_comments_post_url', $default, $post ); return is_string( $template ) && 1 === preg_match( '#^https://[^\s"<>]+$#', $template ) ? $template : $default; } /** * Saves the metabox value. Does nothing without the nonce (REST saves), * on autosaves, on revisions or without `edit_post`. * * @param int $post_id Post ID. * @param \WP_Post $post Post. */ public static function save_metabox( $post_id, $post ) { if ( ! isset( $_POST[ self::NONCE ], $_POST[ self::FIELD ] ) ) { return; } if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST[ self::NONCE ] ) ), 'atproto_comments_save_thread' ) ) { return; } if ( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) || wp_is_post_autosave( $post_id ) || wp_is_post_revision( $post_id ) ) { return; } if ( ! current_user_can( 'edit_post', $post_id ) || ! Settings::is_active_type( $post->post_type ) ) { return; } // Thread_Uri::normalize() only accepts a strict character set. Not // sanitize_text_field(): it strips the %3A of did:web ports. $result = Thread_Link::resolve( (string) wp_unslash( $_POST[ self::FIELD ] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized if ( ! $result['ok'] ) { set_transient( self::notice_key(), $result['error'], MINUTE_IN_SECONDS ); return; } if ( '' === $result['uri'] ) { delete_post_meta( $post_id, self::KEY ); } else { update_post_meta( $post_id, self::KEY, $result['uri'] ); } } /** * Shows and clears the pending error of the current user. */ public static function admin_notices() { $key = self::notice_key(); $error = get_transient( $key ); if ( false === $error ) { return; } delete_transient( $key ); ?>