Пример #1
0
 /**
  * @covers ::bbp_is_reply_spam
  */
 public function test_bbp_is_reply_spam()
 {
     $forum_id = $this->factory->forum->create();
     $topic_id = $this->factory->topic->create(array('post_parent' => $forum_id, 'topic_meta' => array('forum_id' => $forum_id)));
     $reply_id = $this->factory->reply->create(array('post_parent' => $topic_id, 'reply_meta' => array('forum_id' => $forum_id, 'topic_id' => $topic_id)));
     $r = $this->factory->reply->create(array('post_parent' => $topic_id, 'reply_meta' => array('forum_id' => $forum_id, 'topic_id' => $topic_id)));
     bbp_spam_reply($r);
     $reply_spam = bbp_is_reply_spam($r);
     $this->assertTrue($reply_spam);
     bbp_unspam_reply($r);
     $reply_spam = bbp_is_reply_spam($r);
     $this->assertFalse($reply_spam);
 }
Пример #2
0
 /**
  * @covers ::bbp_unspam_reply
  */
 public function test_bbp_unspam_reply()
 {
     // Create a forum
     $f = $this->factory->forum->create();
     // Create a topic
     $t = $this->factory->topic->create(array('post_parent' => $f, 'topic_meta' => array('forum_id' => $f)));
     // Create some replies
     $r = $this->factory->reply->create_many(3, array('post_parent' => $t, 'reply_meta' => array('forum_id' => $f, 'topic_id' => $t)));
     bbp_spam_reply($r[1]);
     $reply_post_status = bbp_get_reply_status($r[1]);
     $this->assertSame('spam', $reply_post_status);
     $reply_spam_meta_status = get_post_meta($r[1], '_bbp_spam_meta_status', true);
     $this->assertSame('publish', $reply_spam_meta_status);
     $topic_reply_count = bbp_get_topic_reply_count($t);
     $this->assertSame('2', $topic_reply_count);
     bbp_unspam_reply($r[1]);
     $reply_post_status = bbp_get_reply_status($r[1]);
     $this->assertSame('publish', $reply_post_status);
     $reply_spam_meta_status = get_post_meta($r[1], '_bbp_spam_meta_status', true);
     $this->assertSame('', $reply_spam_meta_status);
     $topic_reply_count = bbp_get_topic_reply_count($t);
     $this->assertSame('3', $topic_reply_count);
 }
Пример #3
0
 /**
  * Toggle reply
  *
  * Handles the admin-side spamming/unspamming of replies
  *
  * @since 2.0.0 bbPress (r2740)
  *
  * @uses bbp_get_reply() To get the reply
  * @uses current_user_can() To check if the user is capable of editing
  *                           the reply
  * @uses wp_die() To die if the user isn't capable or the post wasn't
  *                 found
  * @uses check_admin_referer() To verify the nonce and check referer
  * @uses bbp_is_reply_spam() To check if the reply is marked as spam
  * @uses bbp_unspam_reply() To unmark the reply as spam
  * @uses bbp_spam_reply() To mark the reply as spam
  * @uses do_action() Calls 'bbp_toggle_reply_admin' with success, post
  *                    data, action and message
  * @uses add_query_arg() To add custom args to the url
  * @uses bbp_redirect() Redirect the page to custom url
  */
 public function toggle_reply()
 {
     if ($this->bail()) {
         return;
     }
     // Only proceed if GET is a reply toggle action
     if (bbp_is_get_request() && !empty($_GET['action']) && in_array($_GET['action'], array('bbp_toggle_reply_spam', 'bbp_toggle_reply_approve')) && !empty($_GET['reply_id'])) {
         $action = $_GET['action'];
         // What action is taking place?
         $reply_id = (int) $_GET['reply_id'];
         // What's the reply id?
         $success = false;
         // Flag
         $post_data = array('ID' => $reply_id);
         // Prelim array
         // Get reply and die if empty
         $reply = bbp_get_reply($reply_id);
         if (empty($reply)) {
             wp_die(__('The reply was not found!', 'bbpress'));
         }
         // What is the user doing here?
         if (!current_user_can('moderate', $reply->ID)) {
             wp_die(__('You do not have the permission to do that!', 'bbpress'));
         }
         switch ($action) {
             case 'bbp_toggle_reply_approve':
                 check_admin_referer('approve-reply_' . $reply_id);
                 $is_approve = bbp_is_reply_pending($reply_id);
                 $message = $is_approve ? 'approved' : 'unapproved';
                 $success = $is_approve ? bbp_approve_reply($reply_id) : bbp_unapprove_reply($reply_id);
                 break;
             case 'bbp_toggle_reply_spam':
                 check_admin_referer('spam-reply_' . $reply_id);
                 $is_spam = bbp_is_reply_spam($reply_id);
                 $message = $is_spam ? 'unspammed' : 'spammed';
                 $success = $is_spam ? bbp_unspam_reply($reply_id) : bbp_spam_reply($reply_id);
                 break;
         }
         $message = array('bbp_reply_toggle_notice' => $message, 'reply_id' => $reply->ID);
         if (false === $success || is_wp_error($success)) {
             $message['failed'] = '1';
         }
         // Do additional reply toggle actions (admin side)
         do_action('bbp_toggle_reply_admin', $success, $post_data, $action, $message);
         // Redirect back to the reply
         $redirect = add_query_arg($message, remove_query_arg(array('action', 'reply_id')));
         bbp_redirect($redirect);
     }
 }
/**
 * Mark a users topics and replies as spam when the user is marked as spam
 *
 * @since bbPress (r3405)
 *
 * @global WPDB $wpdb
 * @param int $user_id Optional. User ID to unspam. Defaults to displayed user.
 *
 * @uses bbp_is_single_user()
 * @uses bbp_is_user_home()
 * @uses bbp_get_displayed_user_id()
 * @uses bbp_is_user_keymaster()
 * @uses get_blogs_of_user()
 * @uses bbp_get_topic_post_type()
 * @uses bbp_get_reply_post_type()
 * @uses switch_to_blog()
 * @uses get_post_type()
 * @uses bbp_unspam_topic()
 * @uses bbp_unspam_reply()
 * @uses restore_current_blog()
 *
 * @return If no user ID passed
 */
function bbp_make_ham_user($user_id = 0)
{
    // Use displayed user if it's not yourself
    if (empty($user_id) && bbp_is_single_user() && !bbp_is_user_home()) {
        $user_id = bbp_get_displayed_user_id();
    }
    // Bail if no user ID
    if (empty($user_id)) {
        return false;
    }
    // Bail if user ID is keymaster
    if (bbp_is_user_keymaster($user_id)) {
        return false;
    }
    // Arm the torpedos
    global $wpdb;
    // Get the blog IDs of the user to mark as spam
    $blogs = get_blogs_of_user($user_id, true);
    // If user has no blogs, they are a guest on this site
    if (empty($blogs)) {
        $blogs[$wpdb->blogid] = array();
    }
    // Make array of post types to mark as spam
    $post_types = array(bbp_get_topic_post_type(), bbp_get_reply_post_type());
    $post_types = "'" . implode("', '", $post_types) . "'";
    // Loop through blogs and remove their posts
    foreach ((array) array_keys($blogs) as $blog_id) {
        // Switch to the blog ID
        switch_to_blog($blog_id);
        // Get topics and replies
        $posts = $wpdb->get_col($wpdb->prepare("SELECT ID FROM {$wpdb->posts} WHERE post_author = %d AND post_status = '%s' AND post_type IN ( {$post_types} )", $user_id, bbp_get_spam_status_id()));
        // Loop through posts and spam them
        if (!empty($posts)) {
            foreach ($posts as $post_id) {
                // The routines for topics ang replies are different, so use the
                // correct one based on the post type
                switch (get_post_type($post_id)) {
                    case bbp_get_topic_post_type():
                        bbp_unspam_topic($post_id);
                        break;
                    case bbp_get_reply_post_type():
                        bbp_unspam_reply($post_id);
                        break;
                }
            }
        }
        // Switch back to current blog
        restore_current_blog();
    }
    // Success
    return true;
}
/**
 * Handles the front end spamming/unspamming and trashing/untrashing/deleting of
 * replies
 *
 * @since bbPress (r2740)
 *
 * @param string $action The requested action to compare this function to
 * @uses bbp_get_reply() To get the reply
 * @uses current_user_can() To check if the user is capable of editing or
 *                           deleting the reply
 * @uses check_ajax_referer() To verify the nonce and check the referer
 * @uses bbp_get_reply_post_type() To get the reply post type
 * @uses bbp_is_reply_spam() To check if the reply is marked as spam
 * @uses bbp_spam_reply() To make the reply as spam
 * @uses bbp_unspam_reply() To unmark the reply as spam
 * @uses wp_trash_post() To trash the reply
 * @uses wp_untrash_post() To untrash the reply
 * @uses wp_delete_post() To delete the reply
 * @uses do_action() Calls 'bbp_toggle_reply_handler' with success, post data
 *                    and action
 * @uses bbp_get_reply_url() To get the reply url
 * @uses wp_safe_redirect() To redirect to the reply
 * @uses bbPress::errors:add() To log the error messages
 */
function bbp_toggle_reply_handler($action = '')
{
    // Bail if required GET actions aren't passed
    if (empty($_GET['reply_id'])) {
        return;
    }
    // Setup possible get actions
    $possible_actions = array('bbp_toggle_reply_spam', 'bbp_toggle_reply_trash');
    // Bail if actions aren't meant for this function
    if (!in_array($action, $possible_actions)) {
        return;
    }
    $failure = '';
    // Empty failure string
    $view_all = false;
    // Assume not viewing all
    $reply_id = (int) $_GET['reply_id'];
    // What's the reply id?
    $success = false;
    // Flag
    $post_data = array('ID' => $reply_id);
    // Prelim array
    // Make sure reply exists
    $reply = bbp_get_reply($reply_id);
    if (empty($reply)) {
        return;
    }
    // What is the user doing here?
    if (!current_user_can('edit_reply', $reply->ID) || 'bbp_toggle_reply_trash' === $action && !current_user_can('delete_reply', $reply->ID)) {
        bbp_add_error('bbp_toggle_reply_permission', __('<strong>ERROR:</strong> You do not have the permission to do that!', 'bbpress'));
        return;
    }
    // What action are we trying to perform?
    switch ($action) {
        // Toggle spam
        case 'bbp_toggle_reply_spam':
            check_ajax_referer('spam-reply_' . $reply_id);
            $is_spam = bbp_is_reply_spam($reply_id);
            $success = $is_spam ? bbp_unspam_reply($reply_id) : bbp_spam_reply($reply_id);
            $failure = $is_spam ? __('<strong>ERROR</strong>: There was a problem unmarking the reply as spam!', 'bbpress') : __('<strong>ERROR</strong>: There was a problem marking the reply as spam!', 'bbpress');
            $view_all = !$is_spam;
            break;
            // Toggle trash
        // Toggle trash
        case 'bbp_toggle_reply_trash':
            $sub_action = in_array($_GET['sub_action'], array('trash', 'untrash', 'delete')) ? $_GET['sub_action'] : false;
            if (empty($sub_action)) {
                break;
            }
            switch ($sub_action) {
                case 'trash':
                    check_ajax_referer('trash-' . bbp_get_reply_post_type() . '_' . $reply_id);
                    $view_all = true;
                    $success = wp_trash_post($reply_id);
                    $failure = __('<strong>ERROR</strong>: There was a problem trashing the reply!', 'bbpress');
                    break;
                case 'untrash':
                    check_ajax_referer('untrash-' . bbp_get_reply_post_type() . '_' . $reply_id);
                    $success = wp_untrash_post($reply_id);
                    $failure = __('<strong>ERROR</strong>: There was a problem untrashing the reply!', 'bbpress');
                    break;
                case 'delete':
                    check_ajax_referer('delete-' . bbp_get_reply_post_type() . '_' . $reply_id);
                    $success = wp_delete_post($reply_id);
                    $failure = __('<strong>ERROR</strong>: There was a problem deleting the reply!', 'bbpress');
                    break;
            }
            break;
    }
    // Do additional reply toggle actions
    do_action('bbp_toggle_reply_handler', $success, $post_data, $action);
    // No errors
    if (false !== $success && !is_wp_error($success)) {
        /** Redirect **********************************************************/
        // Redirect to
        $redirect_to = bbp_get_redirect_to();
        // Get the reply URL
        $reply_url = bbp_get_reply_url($reply_id, $redirect_to);
        // Add view all if needed
        if (!empty($view_all)) {
            $reply_url = bbp_add_view_all($reply_url, true);
        }
        // Redirect back to reply
        wp_safe_redirect($reply_url);
        // For good measure
        exit;
        // Handle errors
    } else {
        bbp_add_error('bbp_toggle_reply', $failure);
    }
}
Пример #6
0
 /**
  * Generic function to test the topic counts on a spammed/unspammed reply
  */
 public function test_bbp_topic_spammed_unspammed_reply_counts()
 {
     $u = $this->factory->user->create();
     $f = $this->factory->forum->create();
     $t = $this->factory->topic->create(array('post_parent' => $f, 'topic_meta' => array('forum_id' => $f)));
     $r = $this->factory->reply->create_many(2, array('post_parent' => $t, 'reply_meta' => array('forum_id' => $f, 'topic_id' => $t)));
     $r3 = $this->factory->reply->create(array('post_parent' => $t, 'post_author' => $u, 'reply_meta' => array('forum_id' => $f, 'topic_id' => $t)));
     $count = bbp_update_topic_reply_count($t);
     $this->assertSame(3, $count);
     $count = bbp_update_topic_reply_count_hidden($t);
     $this->assertSame(0, $count);
     $count = bbp_update_topic_voice_count($t);
     $this->assertSame(2, $count);
     bbp_spam_reply($r3);
     $count = bbp_get_topic_reply_count($t, true);
     $this->assertSame(2, $count);
     $count = bbp_get_topic_reply_count_hidden($t, true);
     $this->assertSame(1, $count);
     $count = bbp_get_topic_voice_count($t, true);
     $this->assertSame(1, $count);
     bbp_unspam_reply($r3);
     $count = bbp_get_topic_reply_count($t, true);
     $this->assertSame(3, $count);
     $count = bbp_get_topic_reply_count_hidden($t, true);
     $this->assertSame(0, $count);
     $count = bbp_get_topic_voice_count($t, true);
     $this->assertSame(2, $count);
 }
Пример #7
0
/**
 * Do the actual reply toggling
 *
 * This function is used by `bbp_toggle_reply_handler()` to do the actual heavy
 * lifting when it comes to toggling replies. It only really makes sense to call
 * within that context, so if you need to call this function directly, make sure
 * you're also doing what the handler does too.
 *
 * @since 2.6.0 bbPress (r6133)
 * @access private
 *
 * @param array $args
 */
function bbp_toggle_reply($args = array())
{
    // Parse the arguments
    $r = bbp_parse_args($args, array('id' => 0, 'action' => '', 'sub_action' => '', 'data' => array()));
    // Build the nonce suffix
    $nonce_suffix = bbp_get_reply_post_type() . '_' . (int) $r['id'];
    // Default return values
    $retval = array('status' => 0, 'message' => '', 'redirect_to' => bbp_get_reply_url($r['id'], bbp_get_redirect_to()), 'view_all' => false);
    // What action are we trying to perform?
    switch ($r['action']) {
        // Toggle approve
        case 'bbp_toggle_reply_approve':
            check_ajax_referer("approve-{$nonce_suffix}");
            $is_approve = bbp_is_reply_pending($r['id']);
            $retval['status'] = $is_approve ? bbp_approve_reply($r['id']) : bbp_unapprove_reply($r['id']);
            $retval['message'] = $is_approve ? __('<strong>ERROR</strong>: There was a problem approving the reply.', 'bbpress') : __('<strong>ERROR</strong>: There was a problem unapproving the reply.', 'bbpress');
            $retval['view_all'] = !$is_approve;
            break;
            // Toggle spam
        // Toggle spam
        case 'bbp_toggle_reply_spam':
            check_ajax_referer("spam-{$nonce_suffix}");
            $is_spam = bbp_is_reply_spam($r['id']);
            $retval['status'] = $is_spam ? bbp_unspam_reply($r['id']) : bbp_spam_reply($r['id']);
            $retval['message'] = $is_spam ? __('<strong>ERROR</strong>: There was a problem unmarking the reply as spam.', 'bbpress') : __('<strong>ERROR</strong>: There was a problem marking the reply as spam.', 'bbpress');
            $retval['view_all'] = !$is_spam;
            break;
            // Toggle trash
        // Toggle trash
        case 'bbp_toggle_reply_trash':
            // Which subaction?
            switch ($r['sub_action']) {
                case 'trash':
                    check_ajax_referer("trash-{$nonce_suffix}");
                    $retval['view_all'] = true;
                    $retval['status'] = wp_trash_post($r['id']);
                    $retval['message'] = __('<strong>ERROR</strong>: There was a problem trashing the reply.', 'bbpress');
                    break;
                case 'untrash':
                    check_ajax_referer("untrash-{$nonce_suffix}");
                    $retval['status'] = wp_untrash_post($r['id']);
                    $retval['message'] = __('<strong>ERROR</strong>: There was a problem untrashing the reply.', 'bbpress');
                    break;
                case 'delete':
                    check_ajax_referer("delete-{$nonce_suffix}");
                    $retval['status'] = wp_delete_post($r['id']);
                    $retval['message'] = __('<strong>ERROR</strong>: There was a problem deleting the reply.', 'bbpress');
                    break;
            }
            break;
    }
    // Add view all if needed
    if (!empty($retval['view_all'])) {
        $retval['redirect_to'] = bbp_add_view_all($retval['redirect_to'], true);
    }
    // Filter & return
    return apply_filters('bbp_toggle_reply', $retval, $r, $args);
}
Пример #8
0
 /**
  * @covers ::bbp_get_topic_freshness_link
  */
 public function test_bbp_get_topic_freshness_link_with_unpublished_replies()
 {
     if (is_multisite()) {
         $this->markTestSkipped('Skipping URL tests in multiste for now.');
     }
     $now = time();
     $post_date = date('Y-m-d H:i:s', $now - 60 * 60 * 20);
     // 2o hours ago
     $post_date_r1 = date('Y-m-d H:i:s', $now - 60 * 60 * 18);
     // 18 hours ago
     $post_date_r2 = date('Y-m-d H:i:s', $now - 60 * 60 * 16);
     // 16 hours ago
     $post_date_r3 = date('Y-m-d H:i:s', $now - 60 * 60 * 14);
     // 14 hours ago
     $post_date_r4 = date('Y-m-d H:i:s', $now - 60 * 60 * 12);
     // 12 hours ago
     $post_date_r5 = date('Y-m-d H:i:s', $now - 60 * 60 * 10);
     // 1o hours ago
     $f = $this->factory->forum->create();
     $t = $this->factory->topic->create(array('post_title' => 'Topic 1', 'post_parent' => $f, 'post_date' => $post_date, 'topic_meta' => array('forum_id' => $f)));
     $link = bbp_get_topic_freshness_link($t);
     $this->assertSame('<a href="http://' . WP_TESTS_DOMAIN . '/?topic=topic-1" title="">20 hours ago</a>', $link);
     $r1 = $this->factory->reply->create(array('post_parent' => $t, 'post_date' => $post_date_r1, 'reply_meta' => array('forum_id' => $f, 'topic_id' => $t)));
     $link = bbp_get_topic_freshness_link($t);
     $this->assertSame('<a href="http://' . WP_TESTS_DOMAIN . '/?topic=topic-1/#post-' . bbp_get_reply_id($r1) . '" title="Reply To: ' . bbp_get_topic_title($t) . '">18 hours ago</a>', $link);
     $r2 = $this->factory->reply->create(array('post_parent' => $t, 'post_date' => $post_date_r2, 'reply_meta' => array('forum_id' => $f, 'topic_id' => $t)));
     $link = bbp_get_topic_freshness_link($t);
     $this->assertSame('<a href="http://' . WP_TESTS_DOMAIN . '/?topic=topic-1/#post-' . bbp_get_reply_id($r2) . '" title="Reply To: ' . bbp_get_topic_title($t) . '">16 hours ago</a>', $link);
     bbp_spam_reply($r2);
     $link = bbp_get_topic_freshness_link($t);
     $this->assertSame('<a href="http://' . WP_TESTS_DOMAIN . '/?topic=topic-1/#post-' . bbp_get_reply_id($r1) . '" title="Reply To: ' . bbp_get_topic_title($t) . '">18 hours ago</a>', $link);
     $r3 = $this->factory->reply->create(array('post_parent' => $t, 'post_date' => $post_date_r3, 'reply_meta' => array('forum_id' => $f, 'topic_id' => $t)));
     $link = bbp_get_topic_freshness_link($t);
     $this->assertSame('<a href="http://' . WP_TESTS_DOMAIN . '/?topic=topic-1/#post-' . bbp_get_reply_id($r3) . '" title="Reply To: ' . bbp_get_topic_title($t) . '">14 hours ago</a>', $link);
     // Todo: Use bbp_trash_reply() and not wp_trash_post()
     wp_trash_post($r3);
     $link = bbp_get_topic_freshness_link($t);
     $this->assertSame('<a href="http://' . WP_TESTS_DOMAIN . '/?topic=topic-1/#post-' . bbp_get_reply_id($r1) . '" title="Reply To: ' . bbp_get_topic_title($t) . '">18 hours ago</a>', $link);
     $r4 = $this->factory->reply->create(array('post_parent' => $t, 'post_date' => $post_date_r4, 'reply_meta' => array('forum_id' => $f, 'topic_id' => $t)));
     $link = bbp_get_topic_freshness_link($t);
     $this->assertSame('<a href="http://' . WP_TESTS_DOMAIN . '/?topic=topic-1/#post-' . bbp_get_reply_id($r4) . '" title="Reply To: ' . bbp_get_topic_title($t) . '">12 hours ago</a>', $link);
     bbp_unapprove_reply($r4);
     $link = bbp_get_topic_freshness_link($t);
     $this->assertSame('<a href="http://' . WP_TESTS_DOMAIN . '/?topic=topic-1/#post-' . bbp_get_reply_id($r1) . '" title="Reply To: ' . bbp_get_topic_title($t) . '">18 hours ago</a>', $link);
     bbp_unspam_reply($r2);
     $link = bbp_get_topic_freshness_link($t);
     $this->assertSame('<a href="http://' . WP_TESTS_DOMAIN . '/?topic=topic-1/#post-' . bbp_get_reply_id($r2) . '" title="Reply To: ' . bbp_get_topic_title($t) . '">16 hours ago</a>', $link);
     // Todo: Use bbp_untrash_reply() and not wp_untrash_post()
     wp_untrash_post($r3);
     $link = bbp_get_topic_freshness_link($t);
     $this->assertSame('<a href="http://' . WP_TESTS_DOMAIN . '/?topic=topic-1/#post-' . bbp_get_reply_id($r3) . '" title="Reply To: ' . bbp_get_topic_title($t) . '">14 hours ago</a>', $link);
     bbp_approve_reply($r4);
     $link = bbp_get_topic_freshness_link($t);
     $this->assertSame('<a href="http://' . WP_TESTS_DOMAIN . '/?topic=topic-1/#post-' . bbp_get_reply_id($r4) . '" title="Reply To: ' . bbp_get_topic_title($t) . '">12 hours ago</a>', $link);
     $r5 = $this->factory->reply->create(array('post_parent' => $t, 'post_date' => $post_date_r5, 'reply_meta' => array('forum_id' => $f, 'topic_id' => $t)));
     $link = bbp_get_topic_freshness_link($t);
     $this->assertSame('<a href="http://' . WP_TESTS_DOMAIN . '/?topic=topic-1/#post-' . bbp_get_reply_id($r5) . '" title="Reply To: ' . bbp_get_topic_title($t) . '">10 hours ago</a>', $link);
 }
Пример #9
0
 public function test_notify_reply()
 {
     $bbpnns = bbPress_Notify_NoSpam::bootstrap();
     // Spam, returns -1
     bbp_spam_reply($this->reply_id);
     $status = $bbpnns->notify_new_reply($this->reply_id, $this->topic_id, $this->forum_id);
     $this->assertEquals(-1, $status, 'Spam reply returns -1');
     // Clear recipients
     $expected_recipients = array();
     update_option('bbpress_notify_newreply_recipients', $recipients);
     // Non-spam, empty recipients returns -2
     bbp_unspam_reply($this->reply_id);
     $status = $bbpnns->notify_new_reply($this->reply_id, $this->topic_id, $this->forum_id);
     $this->assertEquals(-2, $status, 'Empty Recipients -2');
     update_option('bbpress_notify_newreply_email_body', '[reply-content]');
     // Non-spam, non-empty recipents
     update_option('bbpress_notify_newreply_recipients', array('administrator', 'subscriber'));
     $arry = $bbpnns->notify_new_reply($this->reply_id, $this->topic_id, $this->forum_id);
     $this->assertTrue(is_array($arry), 'Good notify returns array in test mode');
     list($recipients, $body) = $arry;
     $reg_body = str_replace('[reply-url]', '[^ ,]+', $this->replyc_body_clean);
     $reg_body = str_replace('[reply-author]', 'admin', $reg_body);
     $reg_body = str_replace('[reply-forum]', 'test-forum', $reg_body);
     $this->assertRegexp("/{$reg_body}/", $body, 'Reply body munged correctly');
     // Force skip
     add_filter('bbpnns_skip_reply_notification', '__return_true');
     $status = $bbpnns->notify_new_reply($this->topic_id);
     $this->assertEquals(-3, $status, 'Force skip -3');
 }