Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/changelog/add-reaction-email-setting
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: added

Add a notification setting to turn off emails about likes, reposts, and quotes without silencing real comments and replies.
12 changes: 12 additions & 0 deletions includes/class-activitypub.php
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,18 @@ public static function register_user_meta() {
);
\add_filter( 'get_user_option_activitypub_mailer_new_mention', array( self::class, 'user_options_default' ) );

\register_meta(
'user',
$blog_prefix . 'activitypub_mailer_new_reaction',
array(
'type' => 'integer',
'description' => 'Send a notification when someone likes, reposts, or quotes this user\'s post.',
'single' => true,
'sanitize_callback' => 'absint',
)
);
\add_filter( 'get_user_option_activitypub_mailer_new_reaction', array( self::class, 'user_options_default' ) );

\register_meta(
'user',
$blog_prefix . 'activitypub_mailer_annual_report',
Expand Down
36 changes: 36 additions & 0 deletions includes/class-mailer.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
namespace Activitypub;

use Activitypub\Collection\Actors;
use Activitypub\Comment;

/**
* Mailer Class.
Expand All @@ -26,6 +27,7 @@ public static function init() {
\add_action( 'activitypub_inbox_create', array( self::class, 'mention' ), 20, 2 ); /** After @see \Activitypub\Handler\Create::handle_create() */

\add_filter( 'notify_post_author', array( self::class, 'maybe_prevent_comment_notification' ), 10, 2 );
\add_filter( 'notify_post_author', array( self::class, 'maybe_prevent_reaction_notification' ), 10, 2 );
\add_filter( 'notify_moderator', array( self::class, 'maybe_prevent_comment_notification' ), 10, 2 );
}

Expand Down Expand Up @@ -554,4 +556,38 @@ public static function maybe_prevent_comment_notification( $maybe_notify, $comme

return $maybe_notify;
}

/**
* Let the post author mute email about reactions to their post.
*
* Likes, reposts, and quotes are stored as comments, so WordPress emails the post author about
* them like any other comment. This is hooked on `notify_post_author` only, so it never affects
* the moderator notification, and it targets the plugin's own reaction comment types so pingbacks,
* trackbacks, and plain replies keep notifying as usual. The preference defaults to on.
*
* @since unreleased
*
* @param bool $maybe_notify Whether to send the notification.
* @param int $comment_id The comment ID.
*
* @return bool Whether to send the notification.
*/
public static function maybe_prevent_reaction_notification( $maybe_notify, $comment_id ) {
// If already disabled, respect that.
if ( ! $maybe_notify ) {
return $maybe_notify;
}

$comment = \get_comment( $comment_id );
if ( ! $comment || ! \in_array( \get_comment_type( $comment ), Comment::get_comment_type_slugs(), true ) ) {
return $maybe_notify;
}

$post = \get_post( $comment->comment_post_ID );
if ( ! $post ) {
return $maybe_notify;
}

return (bool) \get_user_option( 'activitypub_mailer_new_reaction', $post->post_author );
}
}
1 change: 1 addition & 0 deletions includes/wp-admin/class-admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ public static function save_user_settings( $user_id ) {
'activitypub_mailer_new_dm',
'activitypub_mailer_new_follower',
'activitypub_mailer_new_mention',
'activitypub_mailer_new_reaction',
'activitypub_mailer_annual_report',
'activitypub_mailer_monthly_report',
);
Expand Down
6 changes: 6 additions & 0 deletions includes/wp-admin/class-user-settings-fields.php
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,12 @@ public static function notifications_callback() {
<?php \esc_html_e( 'New Mentions', 'activitypub' ); ?>
</label>
</p>
<p>
<label>
<input type="checkbox" name="activitypub_mailer_new_reaction" id="activitypub_mailer_new_reaction" value="1" <?php \checked( 1, \get_user_option( 'activitypub_mailer_new_reaction' ) ); ?> />
<?php \esc_html_e( 'Likes, Reposts and Quotes', 'activitypub' ); ?>
</label>
</p>
<p>
<label>
<input type="checkbox" name="activitypub_mailer_annual_report" id="activitypub_mailer_annual_report" value="1" <?php \checked( 1, \get_user_option( 'activitypub_mailer_annual_report' ) ); ?> />
Expand Down
57 changes: 57 additions & 0 deletions tests/phpunit/tests/includes/class-test-mailer.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,63 @@ public function test_comment_like_notification() {
$this->assertEquals( 'Default Subject', $subject );
}

/**
* The reaction notification setting silences reaction emails (likes, reposts, quotes) without touching real replies.
*
* @covers ::maybe_prevent_reaction_notification
*/
public function test_reaction_notification_setting() {
$like_id = wp_insert_comment(
array(
'comment_post_ID' => self::$post_id,
'comment_type' => 'like',
)
);
$repost_id = wp_insert_comment(
array(
'comment_post_ID' => self::$post_id,
'comment_type' => 'repost',
)
);
$quote_id = wp_insert_comment(
array(
'comment_post_ID' => self::$post_id,
'comment_type' => 'quote',
)
);
$reply_id = wp_insert_comment(
array(
'comment_post_ID' => self::$post_id,
'comment_type' => 'comment',
)
);
$legacy_reply_id = wp_insert_comment(
array(
'comment_post_ID' => self::$post_id,
'comment_type' => '',
)
);

// Default (unset): reactions still notify, so behavior is unchanged for existing users.
$this->assertTrue( Mailer::maybe_prevent_reaction_notification( true, $like_id ) );

update_user_option( self::$user_id, 'activitypub_mailer_new_reaction', '0' );

// Opt out silences every reaction type: like, repost, and quote.
$this->assertFalse( Mailer::maybe_prevent_reaction_notification( true, $like_id ) );
$this->assertFalse( Mailer::maybe_prevent_reaction_notification( true, $repost_id ) );
$this->assertFalse( Mailer::maybe_prevent_reaction_notification( true, $quote_id ) );

// Real replies are never affected, including legacy comments stored with an empty type.
$this->assertTrue( Mailer::maybe_prevent_reaction_notification( true, $reply_id ) );
$this->assertTrue( Mailer::maybe_prevent_reaction_notification( true, $legacy_reply_id ) );

// The moderator notification is never gated by the author's reaction preference.
$this->assertTrue( Mailer::maybe_prevent_comment_notification( true, $like_id ) );

delete_user_option( self::$user_id, 'activitypub_mailer_new_reaction' );
}

/**
* Test comment notification text for ActivityPub comments.
*
Expand Down