しぶとい「extract_reply_context」関数

 失敗例だけどメモっておく。

WordPressのプラグイン「ActivityPub」には、コメント欄からリプライした際に、コメント欄にメンションを記載しなくても、同じスレッド内の全てのアカウントにメンションして通知を送る仕様があり、これを防ぎたいと試行錯誤した。

Fediverseに届いた時に投稿にメンションが記載されないようにすることはできた。次のコードをfunctions.phpに追加する。 

// Fediverseで自動追加メンションが表示されないようにする(ただし、通知は防げない)
function my_filter_comment_text( $content, $comment, $args ) {
    // 常に元のコメントコンテンツで $content を上書きする
    $content = $comment->comment_content;

    // その後の処理は元のフィルターチェーンに沿って行われます
    return $content;
}
add_filter( 'comment_text', 'my_filter_comment_text', 5, 3 );

しかし、この方法では通知は防げない。

自動で付加されるメンションは 'extract_reply_context'という関数で作られている。通知に関しては、この関数が実行されている次の関数が怪しいと思った。

/** * Helper function to get the @-Mentions from the comment content. * * @return array The list of @-Mentions. */ protected function get_mentions() {   \add_filter( 'activitypub_extract_mentions', array( $this, 'extract_reply_context' ) );   /**    * Filter the mentions in the comment.    *    * @param array     $mentions The list of mentions.    * @param string    $content  The content of the comment.    * @param \WP_Comment $comment  The comment object.    *    * @return array The filtered list of mentions.    */ return apply_filters( 'activitypub_extract_mentions', array(), $this->item->comment_content, $this->item ); }

実際、 

\add_filter( 'activitypub_extract_mentions', array( $this, 'extract_reply_context' ) );

をコメントアウトすることで、意図しない通知は防げた。

そこで、Geminiに次のように相談して通知を防ぐコードをfunctions.phpに追加しようとした。

次のコードで、まず、'activitypub_extract_mentions'フィルターから関数'extract_reply_context'を削除する関数remove_extract_reply_contextを作成して、'activitypub_extract_mentions'フィルターにremove_extract_reply_contextを加えて、'activitypub_extract_mentions'フィルターのapply_filtersの際に、一番最初にremove_extract_reply_contextが実行されるようにfunctions.php用のコードを作れますか?

protected function get_mentions() {
  \add_filter( 'activitypub_extract_mentions', array( $this, 'extract_reply_context' ) );
return apply_filters( 'activitypub_extract_mentions', array(), $this->item->comment_content, $this->item );

「一番最初にremove_extract_reply_contextが実行されるように」というのは、'extract_reply_context'が実行されるより前にという意味でした。したがって、'extract_reply_context'が加えられるよりも前に実行されたら困ります。 

その他にも、余計なことを省く注文を繰り返して、作ったもらったコードが次の通り。残念ながら、このコードでは、意図しない通知を防ぐことはできなかった。

if ( ! function_exists( 'remove_extract_reply_context' ) ) {
    /**
     * 'activitypub_extract_mentions'フィルターから'extract_reply_context'関数を削除します。
     *
     * @param array $filters The array of registered filters.
     * @return array The modified array of filters.
     */
    function remove_extract_reply_context( $filters ) {
        foreach ( $filters as $priority => $functions ) {
            foreach ( $functions as $identifier => $function ) {
                if ( ( is_string( $function ) && 'extract_reply_context' === $function ) || ( is_array( $function ) && isset( $function[0] ) && is_object( $function[0] ) && isset( $function[1] ) && 'extract_reply_context' === $function[1] ) ) {
                    unset( $filters[ $priority ][ $identifier ] );
                    if ( empty( $filters[ $priority ] ) ) {
                        unset( $filters[ $priority ] );
                    }
                    break 2; // 両方のループから抜ける
                }
            }
        }
        return $filters;
    }
}

if ( ! function_exists( 'adjust_activitypub_extract_mentions_filter' ) ) {
    /**
     * 'activitypub_extract_mentions'フィルターの実行順序を調整し、
     * 'remove_extract_reply_context'を最初に実行するようにします。
     *
     * @return void
     */
    function adjust_activitypub_extract_mentions_filter() {
        add_filter( 'activitypub_extract_mentions', 'remove_extract_reply_context', 1 );

        // 元のフィルターに追加されていた 'extract_reply_context' を削除
        if ( function_exists( 'remove_filter' ) ) {
            global $wp_filter;
            if ( isset( $wp_filter['activitypub_extract_mentions'] ) ) {
                foreach ( $wp_filter['activitypub_extract_mentions'] as $priority => $callbacks ) {
                    foreach ( $callbacks as $identifier => $callback ) {
                        if ( ( is_string( $callback['function'] ) && 'extract_reply_context' === $callback['function'] ) || ( is_array( $callback['function'] ) && isset( $callback['function'][1] ) && 'extract_reply_context' === $callback['function'][1] ) ) {
                            remove_filter( 'activitypub_extract_mentions', $callback['function'], $priority );
                        }
                    }
                }
            }
        }
    }
    add_action( 'plugins_loaded', 'adjust_activitypub_extract_mentions_filter' );
}

コードの解説のまとめは次の通り。

このコードは、`plugins_loaded` アクション時に `adjust_activitypub_extract_mentions_filter` 関数を実行し、以下の手順で目的を達成します。

1.  `remove_extract_reply_context` 関数を `activitypub_extract_mentions` フィルターに最も早い優先度で登録します。
2.  グローバル変数 `$wp_filter` を直接参照して、元々 `activitypub_extract_mentions` フィルターに登録されていた関数名が `'extract_reply_context'` である関数を特定し、削除します。
3.  `activitypub_extract_mentions` フィルターが適用されると、最初に `remove_extract_reply_context` 関数が実行され、フィルターの配列から `'extract_reply_context'` 関数が削除された状態で、残りのフィルター処理が行われます。

これにより、あなたのテーマやプラグインのfunctions.phpにこのコードを追加することで、`activitypub_extract_mentions` フィルターの `apply_filters` が実行される際に、`'extract_reply_context'` 関数が実行されるのを防ぐことができます。

 

  

コメント