From e6c4c74a0f6ac5a8dca1ea461c6d4bb7f7265fec Mon Sep 17 00:00:00 2001 From: jeherve Date: Mon, 15 Jul 2024 07:41:25 +0000 Subject: [PATCH] Publicize: add Fediverse meta tag on posts when available (#38198) * Publicize: add Fediverse meta tag on posts when available See https://blog.joinmastodon.org/2024/07/highlighting-journalism-on-mastodon/ If we have an existing Mastodon connection on the site, let's add it as meta tag on each post. * Improve logic to support global connections. 1. We now keep all existing Mastodon connections for a post, as long as that post was published to that connection. If you've opted not to share that post to Mastodon, we do not keep that connection. 2. We keep both your own connections as well as any other global connections on the site. 3. We then sort through those connections: * We prioritize your own connections, and pick the first of them, * If you, the author, haven't published that specific post to a Mastodon connection, we look for global Mastodon connections. If there are some, we pick the first. See discussion here: https://github.com/Automattic/jetpack/pull/38198#issuecomment-2225663451 Committed via a GitHub action: https://github.com/Automattic/jetpack/actions/runs/9935380055 Upstream-Ref: Automattic/jetpack@fe17eb182452c2dbb629238f84c8d7921251e3de --- CHANGELOG.md | 3 ++ src/class-publicize-base.php | 93 ++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c2821d..149e8fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 This is an alpha version! The changes listed here are not final. +### Added +- Mastodon: display a Fediverse Creator tag when the post author has connected their account to a Mastodon account. + ### Changed - Social: Removed unnecessary feature checks for social connections diff --git a/src/class-publicize-base.php b/src/class-publicize-base.php index 0266e31..582a9cf 100644 --- a/src/class-publicize-base.php +++ b/src/class-publicize-base.php @@ -254,6 +254,10 @@ public function __construct() { // The custom priority for this action ensures that any existing code that // removes post-thumbnails support during 'init' continues to work. add_action( 'init', __NAMESPACE__ . '\add_theme_post_thumbnails_support', 8 ); + + // Add a Fediverse Open Graph Tag when an author has connected their Mastodon account. + add_filter( 'jetpack_open_graph_tags', array( $this, 'add_fediverse_creator_open_graph_tag' ), 10, 1 ); + add_filter( 'jetpack_open_graph_output', array( $this, 'filter_fediverse_cards_output' ), 10, 1 ); } /** @@ -2116,6 +2120,95 @@ public function get_dismissed_notices() { public static function can_manage_connection( $connection_data ) { return current_user_can( 'edit_others_posts' ) || get_current_user_id() === (int) $connection_data['user_id']; } + + /** + * Display a Fediverse actor Open Graph tag when the post author has a Mastodon connection. + * + * @see https://blog.joinmastodon.org/2024/07/highlighting-journalism-on-mastodon/ + * + * @param array $tags Current tags. + * + * @return array + */ + public function add_fediverse_creator_open_graph_tag( $tags ) { + global $post; + + if ( + ! is_singular() + || ! $post instanceof WP_Post + || ! isset( $post->ID ) + || empty( $post->post_author ) + ) { + return $tags; + } + + $post_mastodon_connections = array(); + + // Loop through active connections. + foreach ( (array) $this->get_services( 'connected' ) as $service_name => $connections ) { + if ( 'mastodon' !== $service_name ) { + continue; + } + + // services can have multiple connections. Store them all in our array. + foreach ( $connections as $connection ) { + $connection_id = $this->get_connection_id( $connection ); + $mastodon_handle = $connection['external_display'] ?? ''; + + if ( empty( $mastodon_handle ) ) { + continue; + } + + // Did we skip this connection for this post? + if ( get_post_meta( $post->ID, $this->POST_SKIP_PUBLICIZE . $connection_id, true ) ) { + continue; + } + + $post_mastodon_connections[] = array( + 'user_id' => (int) $connection['user_id'], + 'connection_id' => (int) $connection_id, + 'handle' => $mastodon_handle, + 'global' => $this->is_global_connection( $connection ), + ); + } + } + + // If we have no Mastodon connections, skip. + if ( empty( $post_mastodon_connections ) ) { + return $tags; + } + + /* + * Select a single Mastodon connection to use. + * It should be either the first connection belonging to the post author, + * or the first global connection. + */ + foreach ( $post_mastodon_connections as $mastodon_connection ) { + if ( $post->post_author === $mastodon_connection['user_id'] ) { + $tags['fediverse:creator'] = esc_attr( $mastodon_connection['handle'] ); + break; + } + + if ( $mastodon_connection['global'] ) { + $tags['fediverse:creator'] = esc_attr( $mastodon_connection['handle'] ); + break; + } + } + + return $tags; + } + + /** + * Update the markup for the Open Graph tag to match the expected output for Mastodon + * (name instead of property). + * + * @param string $og_tag A single OG tag. + * + * @return string Result of the OG tag. + */ + public static function filter_fediverse_cards_output( $og_tag ) { + return ( str_contains( $og_tag, 'fediverse:' ) ) ? preg_replace( '/property="([^"]+)"/', 'name="\1"', $og_tag ) : $og_tag; + } } // phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- TODO: Move these functions to some other file.