diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..0163872 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,25 @@ +# This file is for unifying the coding style for different editors and IDEs. +# It is based on https://core.trac.wordpress.org/browser/trunk/.editorconfig. +# See https://editorconfig.org for more information about the standard. + +# WordPress Coding Standards +# https://make.wordpress.org/core/handbook/coding-standards/ + +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = tab + +[*.yml] +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false + +[*.txt] +end_of_line = crlf diff --git a/.github/workflows/cs-lint.yml b/.github/workflows/cs-lint.yml new file mode 100644 index 0000000..fe4dac4 --- /dev/null +++ b/.github/workflows/cs-lint.yml @@ -0,0 +1,70 @@ +name: CS & Lint + +on: + # Run on all pushes and on all pull requests. + # Prevent the "push" build from running when there are only irrelevant changes. + push: + paths-ignore: + - "**.md" + pull_request: + # Allow manually triggering the workflow. + workflow_dispatch: + +jobs: + checkcs: + name: "Basic CS and QA checks" + runs-on: ubuntu-latest + + env: + XMLLINT_INDENT: " " + + steps: + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "7.4" + coverage: none + tools: cs2pr + + # Show PHP lint violations inline in the file diff. + # @link https://github.com/marketplace/actions/xmllint-problem-matcher + - name: Register PHP lint violations to appear as file diff comments + uses: korelstar/phplint-problem-matcher@v1 + + # Show XML violations inline in the file diff. + # @link https://github.com/marketplace/actions/xmllint-problem-matcher + - name: Register XML violations to appear as file diff comments + uses: korelstar/xmllint-problem-matcher@v1 + + - name: Checkout code + uses: actions/checkout@v2 + + # Validate the composer.json file. + # @link https://getcomposer.org/doc/03-cli.md#validate + - name: Validate Composer installation + run: composer validate --no-check-all + + # Install dependencies and handle caching in one go. + # @link https://github.com/marketplace/actions/install-composer-dependencies + - name: Install Composer dependencies + uses: ramsey/composer-install@v1 + + # Lint PHP. + - name: Lint PHP against parse errors + run: composer lint-ci | cs2pr + + # Needed as runs-on: system doesn't have xml-lint by default. + # @link https://github.com/marketplace/actions/xml-lint + - name: Lint phpunit.xml.dist + uses: ChristophWurst/xmllint-action@v1 + with: + xml-file: ./phpunit.xml.dist + xml-schema-file: ./vendor/phpunit/phpunit/phpunit.xsd + + # Check the code-style consistency of the PHP files. +# - name: Check PHP code style +# continue-on-error: true +# run: vendor/bin/phpcs --report-full --report-checkstyle=./phpcs-report.xml + +# - name: Show PHPCS results in PR +# run: cs2pr ./phpcs-report.xml diff --git a/.github/workflows/integrate.yml b/.github/workflows/integrate.yml deleted file mode 100644 index 7f90c50..0000000 --- a/.github/workflows/integrate.yml +++ /dev/null @@ -1,71 +0,0 @@ -name: Run PHPUnit and PHPCS - -on: [push] - -jobs: - test: - name: WP ${{ matrix.wordpress }} on PHP ${{ matrix.php }} - # Ubuntu-20.x includes MySQL 8.0, which causes `caching_sha2_password` issues with PHP < 7.4 - # https://www.php.net/manual/en/mysqli.requirements.php - # TODO: change to ubuntu-latest when we no longer support PHP < 7.4 - runs-on: ubuntu-18.04 - continue-on-error: ${{ matrix.allowed_failure }} - - env: - WP_VERSION: ${{ matrix.wordpress }} - - strategy: - fail-fast: false - matrix: - php: ["5.6", "7.0", "7.1", "7.2", "7.3", "7.4", "8.0"] - wordpress: ["5.5", "5.6", "5.7"] - allowed_failure: [false] - # https://make.wordpress.org/core/2020/11/23/wordpress-and-php-8-0/ - exclude: - - php: "8.0" - wordpress: "5.5" - - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - name: Set up PHP ${{ matrix.php }} - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php }} - coverage: pcov - # https://make.wordpress.org/hosting/handbook/handbook/server-environment/#php-extensions - extensions: curl, dom, exif, fileinfo, hash, json, mbstring, mysqli, openssl, pcre, imagick, xml, zip - - - name: Install Composer dependencies (PHP < 8.0 ) - if: ${{ matrix.php < 8.0 }} - uses: ramsey/composer-install@v1 - - - name: Install Composer dependencies (PHP >= 8.0) - if: ${{ matrix.php >= 8.0 }} - uses: ramsey/composer-install@v1 - with: - composer-options: --ignore-platform-reqs - - - name: Setup Problem Matchers for PHPUnit - run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" - - - name: Show PHP and PHPUnit version info - run: | - php --version - ./vendor/bin/phpunit --version - - - name: Start MySQL service - run: sudo /etc/init.d/mysql start - - - name: Install WordPress environment - run: composer prepare ${{ matrix.wordpress }} - - - name: Run integration tests (single site) - run: composer integration - - - name: Run integration tests (multisite) - run: composer integration-ms - - - name: Run PHPCS - run: composer cs diff --git a/.github/workflows/integrations.yml b/.github/workflows/integrations.yml new file mode 100644 index 0000000..8f5577e --- /dev/null +++ b/.github/workflows/integrations.yml @@ -0,0 +1,85 @@ +name: Run PHPUnit + +on: + # Run on all pushes and on all pull requests. + # Prevent the "push" build from running when there are only irrelevant changes. + push: + paths-ignore: + - "**.md" + pull_request: + # Allow manually triggering the workflow. + workflow_dispatch: + +jobs: + test: + name: WP ${{ matrix.wordpress }} on PHP ${{ matrix.php }} + # Ubuntu-20.x includes MySQL 8.0, which causes `caching_sha2_password` issues with PHP < 7.4 + # https://www.php.net/manual/en/mysqli.requirements.php + # TODO: change to ubuntu-latest when we no longer support PHP < 7.4 + runs-on: ubuntu-18.04 + + env: + WP_VERSION: ${{ matrix.wordpress }} + + strategy: + matrix: + wordpress: ["5.5", "5.6", "5.7"] + php: ["5.6", "7.0", "7.1", "7.2", "7.3", "7.4"] + include: + - php: "8.0" + # Ignore platform requirements, so that PHPUnit 7.5 can be installed on PHP 8.0 (and above). + composer-options: "--ignore-platform-reqs" + extensions: pcov + ini-values: pcov.directory=., "pcov.exclude=\"~(vendor|tests)~\"" + coverage: pcov + exclude: + - php: "8.0" + wordpress: "5.5" + fail-fast: false + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup PHP ${{ matrix.php }} + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: ${{ matrix.extensions }} + ini-values: ${{ matrix.ini-values }} + coverage: ${{ matrix.coverage }} + + - name: Setup problem matchers for PHP + run: echo "::add-matcher::${{ runner.tool_cache }}/php.json" + + # Setup PCOV since we're using PHPUnit < 8 which has it integrated. Requires PHP 7.1. + # Ignore platform reqs to make it install on PHP 8. + # https://github.com/krakjoe/pcov-clobber + - name: Setup PCOV + if: ${{ matrix.php == 8.0 }} + run: | + composer require pcov/clobber --ignore-platform-reqs + vendor/bin/pcov clobber + + - name: Setup Problem Matchers for PHPUnit + run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" + + - name: Install Composer dependencies + uses: ramsey/composer-install@v1 + with: + composer-options: "${{ matrix.composer-options }}" + + - name: Start MySQL Service + run: sudo systemctl start mysql.service + + - name: Prepare environment for integration tests + run: composer prepare-ci + + - name: Run integration tests (single site) + if: ${{ matrix.php != 8.0 }} + run: composer test + - name: Run integration tests (single site with code coverage) + if: ${{ matrix.php == 8.0 }} + run: composer coverage-ci + - name: Run integration tests (multisite) + run: composer test-ms diff --git a/.gitignore b/.gitignore index 81e9d35..d8a7996 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,2 @@ -.svn -wpcom-helper -.DS_Store -.vscode/ composer.lock vendor/ diff --git a/.phpcs.xml.dist b/.phpcs.xml.dist new file mode 100644 index 0000000..3b843d8 --- /dev/null +++ b/.phpcs.xml.dist @@ -0,0 +1,70 @@ + + + Custom ruleset for safe-report-comments plugin. + + + + + + . + + /vendor/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/class-safe-report-comments.php b/class-safe-report-comments.php new file mode 100644 index 0000000..8b6844d --- /dev/null +++ b/class-safe-report-comments.php @@ -0,0 +1,659 @@ +admin_notices = get_transient( $this->plugin_prefix . '_notices' ); + if ( ! is_array( $this->admin_notices ) ) { + $this->admin_notices = array(); + } + $this->admin_notices = array_unique( $this->admin_notices ); + $this->auto_init = $auto_init; + + if ( ! is_admin() || ( defined( 'DOING_AJAX' ) && true === DOING_AJAX ) ) { + add_action( 'init', array( $this, 'frontend_init' ) ); + } elseif ( is_admin() ) { + add_action( 'admin_init', array( $this, 'backend_init' ) ); + } + add_action( 'comment_unapproved_to_approved', array( $this, 'mark_comment_moderated' ), 10, 1 ); + + /** + * Apply some filters to easily alter the frontend messages. Example: + * add_filter( 'safe_report_comments_thank_you_message', 'alter_message' ); + */ + foreach ( $this->filter_vars as $var ) { + $this->{$var} = apply_filters( 'safe_report_comments_' . $var, $this->{$var} ); + } + } + + /** + * Prevent __destruct + */ + public function __destruct() { + } + + /** + * Initialize backend functions + * - register_admin_panel + * - admin_header + */ + public function backend_init() { + do_action( 'safe_report_comments_backend_init' ); + + add_settings_field( $this->plugin_prefix . '_enabled', __( 'Allow comment flagging', 'safe-report-comments' ), array( $this, 'comment_flag_enable' ), 'discussion', 'default' ); + register_setting( 'discussion', $this->plugin_prefix . '_enabled' ); + + if ( ! $this->is_enabled() ) { + return; + } + + add_settings_field( $this->plugin_prefix . '_threshold', __( 'Flagging threshold', 'safe-report-comments' ), array( $this, 'comment_flag_threshold' ), 'discussion', 'default' ); + register_setting( 'discussion', $this->plugin_prefix . '_threshold', array( $this, 'check_threshold' ) ); + add_filter( 'manage_edit-comments_columns', array( $this, 'add_comment_reported_column' ) ); + add_action( 'manage_comments_custom_column', array( $this, 'manage_comment_reported_column' ), 10, 2 ); + + add_action( 'admin_menu', array( $this, 'register_admin_panel' ) ); + add_action( 'admin_head', array( $this, 'admin_header' ) ); + } + + /** + * Initialize frontend functions + */ + public function frontend_init() { + if ( ! $this->is_enabled() ) { + return; + } + + if ( ! $this->plugin_url ) { + $this->plugin_url = plugins_url( false, __FILE__ ); + } + + do_action( 'safe_report_comments_frontend_init' ); + + add_action( 'wp_ajax_safe_report_comments_flag_comment', array( $this, 'flag_comment' ) ); + add_action( 'wp_ajax_nopriv_safe_report_comments_flag_comment', array( $this, 'flag_comment' ) ); + + add_action( 'wp_enqueue_scripts', array( $this, 'action_enqueue_scripts' ) ); + + if ( $this->auto_init ) { + add_filter( 'comment_reply_link', array( $this, 'add_flagging_link' ) ); + } + add_action( 'comment_report_abuse_link', array( $this, 'print_flagging_link' ) ); + + add_action( 'template_redirect', array( $this, 'add_test_cookie' ) ); // need to do this at template_redirect because is_feed isn't available yet. + } + + /** + * Enqueues scripts on front end. + */ + public function action_enqueue_scripts() { + + // Use home_url() if domain mapped to avoid cross-domain issues. + if ( home_url() != site_url() ) { + $ajaxurl = home_url( '/wp-admin/admin-ajax.php' ); + } else { + $ajaxurl = admin_url( 'admin-ajax.php' ); + } + + $ajaxurl = apply_filters( 'safe_report_comments_ajax_url', $ajaxurl ); + + wp_enqueue_script( $this->plugin_prefix . '-ajax-request', $this->plugin_url . '/js/ajax.js', array( 'jquery' ), '1.0', true ); + wp_localize_script( $this->plugin_prefix . '-ajax-request', 'SafeCommentsAjax', array( 'ajaxurl' => $ajaxurl ) ); // slightly dirty but needed due to possible problems with mapped domains. + } + + /** + * Set a cookie now to see if they are supported by the browser. + * Don't add cookie if it's already set; and don't do it for feeds. + */ + public function add_test_cookie() { + if ( ! is_feed() && ! isset( $_COOKIE[ TEST_COOKIE ] ) ) { + @setcookie( TEST_COOKIE, 'WP Cookie check', 0, COOKIEPATH, COOKIE_DOMAIN ); + if ( SITECOOKIEPATH != COOKIEPATH ) { + @setcookie( TEST_COOKIE, 'WP Cookie check', 0, SITECOOKIEPATH, COOKIE_DOMAIN ); + } + } + } + + /** + * Add necessary header scripts. + * Currently only used for admin notices. + */ + public function admin_header() { + // print admin notice in case of notice strings given. + if ( ! empty( $this->admin_notices ) ) { + add_action( 'admin_notices', array( $this, 'print_admin_notice' ) ); + } + echo ''; + } + + /** + * Add admin error messages. + * + * @param string $message Admin notice text. + */ + protected function add_admin_notice( $message ) { + $this->admin_notices[] = $message; + set_transient( $this->plugin_prefix . '_notices', $this->admin_notices, 3600 ); + } + + /** + * Print a notification / error msg. + */ + public function print_admin_notice() { + ?> +

+ admin_notices as $notice ) { + ?> +

+ +
+ admin_notices = array(); + delete_transient( $this->plugin_prefix . '_notices' ); + } + + /** + * Callback for settings field. + */ + public function comment_flag_enable() { + $enabled = $this->is_enabled(); + ?> + + plugin_prefix . '_threshold' ); + ?> + + plugin_prefix . '_enabled' ); + if ( 1 == $enabled ) { + $enabled = true; + } else { + $enabled = false; + } + return $enabled; + } + + /** + * Validate threshold, callback for settings field. + * + * @param int $value Threshold value. + * @return int Valid threshold value between 1 and 100. + */ + public function check_threshold( $value ) { + if ( (int) $value <= 0 || (int) $value > 100 ) { + $this->add_admin_notice( __( 'Please revise your flagging threshold and enter a number between 1 and 100', 'safe-report-comments' ) ); + } + return (int) $value; + } + + /** + * Helper function to serialize cookie values. + * + * @param array $value Cookie data. + * @return string Encoded cookie data. + */ + private function serialize_cookie( $value ) { + $value = $this->clean_cookie_data( $value ); + return base64_encode( wp_json_encode( $value ) ); + } + + /** + * Helper function to unserialize cookie values. + * + * @param string $value Encoded cookie data. + * @return array Decoded cookie data. + */ + private function unserialize_cookie( $value ) { + $data = json_decode( base64_decode( $value ) ); + return $this->clean_cookie_data( $data ); + } + + /** + * Validate cookie data for numeric values. + * + * @param mixed $data Given cookie data. + * @return array Cookie data array with only numeric keys and values. + */ + private function clean_cookie_data( $data ) { + $clean_data = array(); + + if ( ! is_array( $data ) ) { + $data = array(); + } + + foreach ( $data as $comment_id => $count ) { + if ( is_numeric( $comment_id ) && is_numeric( $count ) ) { + $clean_data[ $comment_id ] = $count; + } + } + + return $clean_data; + } + + /** + * Mark a comment as being moderated so it will not be autoflagged again. + * + * Called via comment transient from unapproved to approved. + * + * @param WP_Comment $comment Comment to mark. + */ + public function mark_comment_moderated( $comment ) { + if ( isset( $comment->comment_ID ) ) { + update_comment_meta( $comment->comment_ID, $this->plugin_prefix . '_moderated', true ); + } + } + + /** + * Check if this comment was flagged by the user before. + * + * @param int $comment_id Comment to check. + * @return bool Whether comment has already been flagged by user. + */ + public function already_flagged( $comment_id ) { + + // check if cookies are enabled and use cookie store. + if ( isset( $_COOKIE[ TEST_COOKIE ] ) ) { + if ( isset( $_COOKIE[ $this->storagecookie ] ) ) { + $data = $this->unserialize_cookie( sanitize_text_field( $_COOKIE[ $this->storagecookie ] ) ); + if ( is_array( $data ) && isset( $data[ $comment_id ] ) ) { + return true; + } + } + } + + $remote_addr = filter_input( INPUT_SERVER, 'REMOTE_ADDR', FILTER_VALIDATE_IP ); + $remote_addr = sanitize_text_field( $remote_addr ); + + // in case we don't have cookies. fall back to transients, block based on IP/User Agent. + $transient = get_transient( md5( $this->storagecookie . $remote_addr ) ); + if ( $transient ) { + if ( + // check if no cookie and transient is set. + ( ! isset( $_COOKIE[ TEST_COOKIE ] ) && isset( $transient[ $comment_id ] ) ) || + // or check if cookies are enabled and comment is not flagged but transients show a relatively high number and assume fraud. + ( isset( $_COOKIE[ TEST_COOKIE ] ) && isset( $transient[ $comment_id ] ) && $transient[ $comment_id ] >= $this->no_cookie_grace ) + ) { + return true; + } + } + return false; + } + + /** + * Report a comment and send it to moderation if threshold is reached. + * + * @param int $comment_id Comment to mark. + */ + public function mark_flagged( $comment_id ) { + $data = array(); + if ( isset( $_COOKIE[ TEST_COOKIE ] ) ) { + if ( isset( $_COOKIE[ $this->storagecookie ] ) ) { + $data = $this->unserialize_cookie( sanitize_text_field( $_COOKIE[ $this->storagecookie ] ) ); + if ( ! isset( $data[ $comment_id ] ) ) { + $data[ $comment_id ] = 0; + } + $data[ $comment_id ]++; + $cookie = $this->serialize_cookie( $data ); + @setcookie( $this->storagecookie, $cookie, time() + $this->cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN ); + if ( SITECOOKIEPATH != COOKIEPATH ) { + @setcookie( $this->storagecookie, $cookie, time() + $this->cookie_lifetime, SITECOOKIEPATH, COOKIE_DOMAIN ); + } + } else { + if ( ! isset( $data[ $comment_id ] ) ) { + $data[ $comment_id ] = 0; + } + $data[ $comment_id ]++; + $cookie = $this->serialize_cookie( $data ); + @setcookie( $this->storagecookie, $cookie, time() + $this->cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN ); + if ( SITECOOKIEPATH != COOKIEPATH ) { + @setcookie( $this->storagecookie, $cookie, time() + $this->cookie_lifetime, SITECOOKIEPATH, COOKIE_DOMAIN ); + } + } + } + + $remote_addr = filter_input( INPUT_SERVER, 'REMOTE_ADDR', FILTER_VALIDATE_IP ); + $remote_addr = sanitize_text_field( $remote_addr ); + + // in case we don't have cookies. fall back to transients, block based on IP, shorter timeout to keep mem usage low and don't lock out whole companies. + $transient = get_transient( md5( $this->storagecookie . $remote_addr ) ); + if ( ! $transient ) { + set_transient( md5( $this->storagecookie . $remote_addr ), array( $comment_id => 1 ), $this->transient_lifetime ); + } else { + if ( ! isset( $transient[ $comment_id ] ) ) { + $transient[ $comment_id ] = 0; + } + $transient[ $comment_id ]++; + set_transient( md5( $this->storagecookie . $remote_addr ), $transient, $this->transient_lifetime ); + } + + + $threshold = (int) get_option( $this->plugin_prefix . '_threshold' ); + $current_reports = get_comment_meta( $comment_id, $this->plugin_prefix . '_reported', true ); + $current_reports++; + update_comment_meta( $comment_id, $this->plugin_prefix . '_reported', $current_reports ); + + + // we will not flag a comment twice. the moderator is the boss here. + $already_reported = get_comment_meta( $comment_id, $this->plugin_prefix . '_reported', true ); + $already_moderated = get_comment_meta( $comment_id, $this->plugin_prefix . '_moderated', true ); + if ( true == $already_reported && true == $already_moderated ) { + // But maybe the boss wants to allow comments to be reflagged. + if ( ! apply_filters( 'safe_report_comments_allow_moderated_to_be_reflagged', false ) ) { + return; + } + } + + if ( $current_reports >= $threshold ) { + do_action( 'safe_report_comments_mark_flagged', $comment_id ); + wp_set_comment_status( $comment_id, 'hold' ); + } + } + + /** + * Die() with or without screen based on JS availability. + * + * @param string $message Message to print. + */ + private function cond_die( $message ) { + // phpcs:ignore WordPress.Security.NonceVerification.Recommended + if ( isset( $_REQUEST['no_js'] ) && true == (bool) $_REQUEST['no_js'] ) { + wp_die( esc_html( $message ), esc_html__( 'Safe Report Comments Notice', 'safe-report-comments' ), array( 'response' => 200 ) ); + } else { + die( esc_html( $message ) ); + } + } + + /** + * Ajax callback to flag/report a comment. + * + * @todo Confirm this callback only receives POST data + */ + public function flag_comment() { + if ( empty( $_REQUEST['comment_id'] ) || (int) $_REQUEST['comment_id'] != $_REQUEST['comment_id'] ) { + $this->cond_die( $this->invalid_values_message ); + } + + $comment_id = (int) $_REQUEST['comment_id']; + if ( $this->already_flagged( $comment_id ) ) { + $this->cond_die( $this->already_flagged_message ); + } + + // checking if nonces help. + if ( ! isset( $_REQUEST['sc_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( $_REQUEST['sc_nonce'] ), $this->plugin_prefix . '_' . $this->nonce_key ) ) { + $this->cond_die( $this->invalid_nonce_message ); + } else { + $this->mark_flagged( $comment_id ); + $this->cond_die( $this->thank_you_message ); + } + } + + /** + * Print the link for flagging comments. + * + * @param int $comment_id The comment ID. + * @param string $result_id Used as attribute ID in markup. + * @param string $text Text of link. + */ + public function print_flagging_link( $comment_id = '', $result_id = '', $text = 'Report comment' ) { + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaping done in get_flagging_link + echo $this->get_flagging_link( $comment_id, $result_id, $text ); + } + + /** + * Output Link to report a comment + * + * @param int $comment_id The comment ID. + * @param string $result_id Used as attribute ID in markup. + * @param string $text Text of link. + */ + public function get_flagging_link( $comment_id = '', $result_id = '', $text = 'Report comment' ) { + global $in_comment_loop; + if ( empty( $comment_id ) && ! $in_comment_loop ) { + return esc_html__( 'Wrong usage of print_flagging_link().', 'safe-report-comments' ); + } + if ( empty( $comment_id ) ) { + $comment_id = get_comment_ID(); + } else { + $comment_id = (int) $comment_id; + if ( ! get_comment( $comment_id ) ) { + return esc_html__( 'This comment does not exist.', 'safe-report-comments' ); + } + } + if ( empty( $result_id ) ) { + $result_id = 'safe-comments-result-' . $comment_id; + } + + $result_id = apply_filters( 'safe_report_comments_result_id', $result_id ); + $text = apply_filters( 'safe_report_comments_flagging_link_text', $text ); + + $nonce = wp_create_nonce( $this->plugin_prefix . '_' . $this->nonce_key ); + $params = array( + 'action' => 'safe_report_comments_flag_comment', + 'sc_nonce' => $nonce, + 'comment_id' => $comment_id, + 'result_id' => $result_id, + 'no_js' => true, + ); + + if ( $this->already_flagged( $comment_id ) ) { + return esc_html( $this->already_flagged_note ); + } + + // @todo Confirm that $result_id is unnecessary in JS call (and its associated ajax callback). + return apply_filters( + 'safe_report_comments_flagging_link', + ' + ' . esc_html( $text ) . '' + ); + } + + /** + * Callback function to automatically hook in the report link after the comment reply link. + * If you want to control the placement on your own define no_autostart_safe_report_comments in your functions.php file and initialize the class + * with $safe_report_comments = new Safe_Report_Comments( $auto_init = false ); + * + * @param string $comment_reply_link Comment reply link markup. + * @return string Modified comment reply link markup. + */ + public function add_flagging_link( $comment_reply_link ) { + if ( ! preg_match_all( '#^(.*)(]+>)(.+)()(.*)$#msiU', $comment_reply_link, $matches ) ) { + return '' . $comment_reply_link; + } + + $comment_reply_link = $matches[1][0] . $matches[2][0] . $matches[4][0] . $matches[5][0] . '' . $this->get_flagging_link() . '' . $matches[6][0]; + return apply_filters( 'safe_report_comments_comment_reply_link', $comment_reply_link ); + } + + /** + * Callback function to add the report counter to comments screen. Remove action manage_edit-comments_columns if not desired. + * + * @param array $comment_columns Comments screen columns. + * @return array Modified comments screen columns. + */ + public function add_comment_reported_column( $comment_columns ) { + $comment_columns['comment_reported'] = _x( 'Reported', 'column name', 'safe-report-comments' ); + return $comment_columns; + } + + /** + * Callback function to handle custom column. remove action manage_comments_custom_column if not desired. + * + * @param string $column_name Column name. + * @param int $comment_id Comment ID. + */ + public function manage_comment_reported_column( $column_name, $comment_id ) { + switch ( $column_name ) { + case 'comment_reported': + $reports = 0; + $already_reported = get_comment_meta( $comment_id, $this->plugin_prefix . '_reported', true ); + if ( $already_reported > 0 ) { + $reports = (int) $already_reported; + } + echo esc_html( $reports ); + break; + default: + break; + } + } + +} diff --git a/composer.json b/composer.json index faa7a70..23c4bac 100644 --- a/composer.json +++ b/composer.json @@ -1,55 +1,61 @@ { - "name": "automattic/safe-report-comments", - "description": "Allows visitors to report a comment as inappropriate.", - "homepage": "https://github.com/Automattic/safe-report-comments/", - "type": "wordpress-plugin", - "license": "GPL-2.0+", - "authors": [ - { - "name": "Automattic", - "homepage": "http://automattic.com/" - } - ], - "support": { - "issues": "https://github.com/Automattic/safe-report-comments/issues", - "source": "https://github.com/Automattic/safe-report-comments" - }, - "require": { - "composer/installers": "~1.0", - "php": ">=5.6" - }, - "require-dev": { - "automattic/vipwpcs": "^2.2", - "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7", - "php-parallel-lint/php-parallel-lint": "^1.0", - "phpcompatibility/phpcompatibility-wp": "^2.1", - "phpunit/phpunit": "^4 || ^5 || ^6 || ^7", - "squizlabs/php_codesniffer": "^3.5", - "wp-coding-standards/wpcs": "^2.3.0", - "yoast/phpunit-polyfills": "^0.2.0" - }, - "scripts": { - "cs": [ - "@php ./vendor/bin/phpcs -p -s -v -n . --standard=\"WordPress-VIP-Go\" --extensions=php --ignore=\"/vendor/*,/node_modules/*,/tests/*\"" - ], - "cbf": [ - "@php ./vendor/bin/phpcbf -p -s -v -n . --standard=\"WordPress-VIP-Go\" --extensions=php --ignore=\"/vendor/*,/node_modules/*,/tests/*\"" - ], - "lint": [ - "@php ./vendor/php-parallel-lint/php-parallel-lint/parallel-lint . -e php --exclude vendor --exclude .git" - ], - "lint-ci": [ - "@php ./vendor/php-parallel-lint/php-parallel-lint/parallel-lint . -e php --exclude vendor --exclude .git --checkstyle" - ], - "prepare": [ - "bash bin/install-wp-tests.sh wordpress_test root root localhost" - ], - "integration": [ - "@php ./vendor/bin/phpunit --testsuite WP_Tests" - ], - "integration-ms": [ - "@putenv WP_MULTISITE=1", - "@composer integration" - ] - } + "name": "automattic/safe-report-comments", + "type": "wordpress-plugin", + "description": "Allows visitors to report a comment as inappropriate.", + "homepage": "https://github.com/Automattic/safe-report-comments/", + "license": "GPL-2.0-or-later", + "authors": [ + { + "name": "Automattic", + "homepage": "https://automattic.com/" + } + ], + "require": { + "php": ">=5.6", + "composer/installers": "~1.0" + }, + "require-dev": { + "automattic/vipwpcs": "^2.2", + "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7", + "php-parallel-lint/php-parallel-lint": "^1.0", + "phpcompatibility/phpcompatibility-wp": "^2.1", + "phpunit/phpunit": "^4 || ^5 || ^6 || ^7", + "squizlabs/php_codesniffer": "^3.5", + "wp-coding-standards/wpcs": "^2.3.0", + "yoast/phpunit-polyfills": "^0.2.0" + }, + "scripts": { + "cbf": [ + "@php ./vendor/bin/phpcbf" + ], + "coverage": [ + "@php ./vendor/bin/phpunit --coverage-html ./build/coverage-html" + ], + "coverage-ci": [ + "@php ./vendor/bin/phpunit" + ], + "cs": [ + "@php ./vendor/bin/phpcs" + ], + "lint": [ + "@php ./vendor/php-parallel-lint/php-parallel-lint/parallel-lint . -e php --exclude vendor --exclude .git" + ], + "lint-ci": [ + "@php ./vendor/php-parallel-lint/php-parallel-lint/parallel-lint . -e php --exclude vendor --exclude .git --checkstyle" + ], + "prepare-ci": [ + "bash bin/install-wp-tests.sh wordpress_test root root localhost" + ], + "test": [ + "@php ./vendor/bin/phpunit --testsuite WP_Tests" + ], + "test-ms": [ + "@putenv WP_MULTISITE=1", + "@composer test" + ] + }, + "support": { + "issues": "https://github.com/Automattic/safe-report-comments/issues", + "source": "https://github.com/Automattic/safe-report-comments" + } } diff --git a/phpunit.xml b/phpunit.xml.dist similarity index 100% rename from phpunit.xml rename to phpunit.xml.dist diff --git a/safe-report-comments.php b/safe-report-comments.php index 073aa60..032eb8e 100644 --- a/safe-report-comments.php +++ b/safe-report-comments.php @@ -10,497 +10,13 @@ * Author URI: http://automattic.com * License: GPLv2 * - * @phpcs:disable WordPressVIPMinimum.Functions.RestrictedFunctions.cookies_setcookie - * @phpcs:disable WordPressVIPMinimum.Variables.RestrictedVariables.cache_constraints___COOKIE + * @package Safe_Report_Comments */ -if ( !class_exists( "Safe_Report_Comments" ) ) { - - class Safe_Report_Comments { - - private $_plugin_prefix = 'srcmnt'; - private $_admin_notices = array(); - private $_nonce_key = 'flag_comment_nonce'; - private $_auto_init = true; - private $_storagecookie = 'sfrc_flags'; - - public $plugin_url = false; - - public $thank_you_message = 'Thank you for your feedback. We will look into it.'; - public $invalid_nonce_message = 'It seems you already reported this comment. '; - public $invalid_values_message = 'Cheating huh? '; - public $already_flagged_message = 'It seems you already reported this comment. '; - public $already_flagged_note = ''; // displayed instead of the report link when a comment was flagged. - - public $filter_vars = array( 'thank_you_message', 'invalid_nonce_message', 'invalid_values_message', 'already_flagged_message', 'already_flagged_note' ); - - // amount of possible attempts transient hits per comment before a COOKIE enabled negative check is considered invalid - // transient hits will be counted up per ip any time a user flags a comment - // this number should be always lower than your threshold to avoid manipulation - public $no_cookie_grace = 3; - public $cookie_lifetime = 604800; // lifetime of the cookie ( 1 week ). After this duration a user can report a comment again - public $transient_lifetime = 86400; // lifetime of fallback transients. lower to keep things usable and c - - public function __construct( $auto_init=true ) { - - $this->_admin_notices = get_transient( $this->_plugin_prefix . '_notices' ); - if ( ! is_array( $this->_admin_notices ) ) { - $this->_admin_notices = array(); - } - $this->_admin_notices = array_unique( $this->_admin_notices ); - $this->_auto_init = $auto_init; - - if ( !is_admin() || ( defined( 'DOING_AJAX' ) && true === DOING_AJAX ) ) { - add_action( 'init', array( $this, 'frontend_init' ) ); - } else if ( is_admin() ) { - add_action( 'admin_init', array( $this, 'backend_init' ) ); - } - add_action( 'comment_unapproved_to_approved', array( $this, 'mark_comment_moderated' ), 10, 1 ); - - // apply some filters to easily alter the frontend messages - // add_filter( 'safe_report_comments_thank_you_message', 'alter_message' ); // this or similar will do the job - foreach ( $this->filter_vars as $var ) { - $this->{$var} = apply_filters( 'safe_report_comments_' . $var , $this->{$var} ); - } - } - - public function __destruct() { - - } - - /* - * Initialize backend functions - * - register_admin_panel - * - admin_header - */ - public function backend_init() { - do_action( 'safe_report_comments_backend_init' ); - - add_settings_field( $this->_plugin_prefix . '_enabled', __( 'Allow comment flagging' ), array( $this, 'comment_flag_enable' ), 'discussion', 'default' ); - register_setting( 'discussion', $this->_plugin_prefix . '_enabled' ); - - if ( ! $this->is_enabled() ) { - return; - } - - add_settings_field( $this->_plugin_prefix . '_threshold', __( 'Flagging threshold' ), array( $this, 'comment_flag_threshold' ), 'discussion', 'default' ); - register_setting( 'discussion', $this->_plugin_prefix . '_threshold', array( $this, 'check_threshold' ) ); - add_filter('manage_edit-comments_columns', array( $this, 'add_comment_reported_column' ) ); - add_action('manage_comments_custom_column', array( $this, 'manage_comment_reported_column' ), 10, 2); - - add_action( 'admin_menu', array( $this, 'register_admin_panel' ) ); - add_action( 'admin_head', array( $this, 'admin_header' ) ); - } - - /* - * Initialize frontend functions - */ - public function frontend_init() { - - if ( ! $this->is_enabled() ) { - return; - } - - if ( ! $this->plugin_url ) { - $this->plugin_url = plugins_url( false, __FILE__ ); - } - - do_action( 'safe_report_comments_frontend_init' ); - - add_action( 'wp_ajax_safe_report_comments_flag_comment', array( $this, 'flag_comment' ) ); - add_action( 'wp_ajax_nopriv_safe_report_comments_flag_comment', array( $this, 'flag_comment' ) ); - - add_action( 'wp_enqueue_scripts', array( $this, 'action_enqueue_scripts' ) ); - - if ( $this->_auto_init ) { - add_filter( 'comment_reply_link', array( $this, 'add_flagging_link' ) ); - } - add_action( 'comment_report_abuse_link', array( $this, 'print_flagging_link' ) ); - - add_action( 'template_redirect', array( $this, 'add_test_cookie' ) ); // need to do this at template_redirect because is_feed isn't available yet - } - - public function action_enqueue_scripts() { - - // Use home_url() if domain mapped to avoid cross-domain issues - if ( home_url() != site_url() ) { - $ajaxurl = home_url( '/wp-admin/admin-ajax.php' ); - } else { - $ajaxurl = admin_url( 'admin-ajax.php' ); - } - - $ajaxurl = apply_filters( 'safe_report_comments_ajax_url', $ajaxurl ); - - wp_enqueue_script( $this->_plugin_prefix . '-ajax-request', $this->plugin_url . '/js/ajax.js', array( 'jquery' ) ); - wp_localize_script( $this->_plugin_prefix . '-ajax-request', 'SafeCommentsAjax', array( 'ajaxurl' => $ajaxurl ) ); // slightly dirty but needed due to possible problems with mapped domains - } - - public function add_test_cookie() { - //Set a cookie now to see if they are supported by the browser. - // Don't add cookie if it's already set; and don't do it for feeds - if ( ! is_feed() && ! isset( $_COOKIE[ TEST_COOKIE ] ) ) { - @setcookie(TEST_COOKIE, 'WP Cookie check', 0, COOKIEPATH, COOKIE_DOMAIN); - if ( SITECOOKIEPATH != COOKIEPATH ) { - @setcookie(TEST_COOKIE, 'WP Cookie check', 0, SITECOOKIEPATH, COOKIE_DOMAIN); - } - } - } - - /* - * Add necessary header scripts - * Currently only used for admin notices - */ - public function admin_header() { - // print admin notice in case of notice strings given - if ( !empty( $this->_admin_notices ) ) { - add_action('admin_notices' , array( $this, 'print_admin_notice' ) ); - } -?> - -_admin_notices[] = $message; - set_transient( $this->_plugin_prefix . '_notices', $this->_admin_notices, 3600 ); - } - - /* - * Print a notification / error msg - */ - public function print_admin_notice() { - ?>

Safe Comments:

_admin_notices as $notice ) { - ?> -

-
_admin_notices = array(); - delete_transient( $this->_plugin_prefix . '_notices' ); - } - - /* - * Callback for settings field - */ - public function comment_flag_enable() { - $enabled = $this->is_enabled(); - ?> - - _plugin_prefix . '_threshold' ); - ?> - - _plugin_prefix . '_enabled' ); - if ( $enabled == 1 ) { - $enabled = true; - } else { - $enabled = false; - } - return $enabled; - } - - /* - * Validate threshold, callback for settings field - */ - public function check_threshold( $value ) { - if ( (int) $value <= 0 || (int) $value > 100 ) { - $this->add_admin_notice( __('Please revise your flagging threshold and enter a number between 1 and 100') ); - } - return (int) $value; - } - - /* - * Helper functions to (un)/serialize cookie values - */ - private function serialize_cookie( $value ) { - $value = $this->clean_cookie_data( $value ); - return base64_encode( json_encode( $value ) ); - } - private function unserialize_cookie( $value ) { - $data = json_decode( base64_decode( $value ) ); - return $this->clean_cookie_data( $data ); - } - - private function clean_cookie_data( $data ) { - $clean_data = array(); - - if ( ! is_array( $data ) ) { - $data = array(); - } - - foreach ( $data as $comment_id => $count ) { - if ( is_numeric( $comment_id ) && is_numeric( $count ) ) { - $clean_data[ $comment_id ] = $count; - } - } - - return $clean_data; - } - - /* - * Mark a comment as being moderated so it will not be autoflagged again - * called via comment transient from unapproved to approved - */ - public function mark_comment_moderated( $comment ) { - if ( isset( $comment->comment_ID ) ) { - update_comment_meta( $comment->comment_ID, $this->_plugin_prefix . '_moderated', true ); - } - } - - /* - * Check if this comment was flagged by the user before - */ - public function already_flagged( $comment_id ) { - - // check if cookies are enabled and use cookie store - if ( isset( $_COOKIE[ TEST_COOKIE ] ) ) { - if ( isset( $_COOKIE[ $this->_storagecookie ] ) ) { - $data = $this->unserialize_cookie( $_COOKIE[ $this->_storagecookie ] ); - if ( is_array( $data ) && isset( $data[ $comment_id ] ) ) { - return true; - } - } - } - - $remote_addr = filter_input( INPUT_SERVER, 'REMOTE_ADDR', FILTER_VALIDATE_IP ); - $remote_addr = sanitize_text_field( $remote_addr ); - - // in case we don't have cookies. fall back to transients, block based on IP/User Agent - if ( $transient = get_transient( md5( $this->_storagecookie . $remote_addr ) ) ) { - if ( - // check if no cookie and transient is set - ( !isset( $_COOKIE[ TEST_COOKIE ] ) && isset( $transient[ $comment_id ] ) ) || - // or check if cookies are enabled and comment is not flagged but transients show a relatively high number and assume fraud - ( isset( $_COOKIE[ TEST_COOKIE ] ) && isset( $transient[ $comment_id ] ) && $transient[ $comment_id ] >= $this->no_cookie_grace ) - ) { - return true; - } - } - return false; - } - - /* - * Report a comment and send it to moderation if threshold is reached - */ - public function mark_flagged( $comment_id ) { - $data = array(); - if ( isset( $_COOKIE[ TEST_COOKIE ] ) ) { - if ( isset( $_COOKIE[ $this->_storagecookie ] ) ) { - $data = $this->unserialize_cookie( $_COOKIE[ $this->_storagecookie ] ); - if ( ! isset( $data[ $comment_id ] ) ) { - $data[ $comment_id ] = 0; - } - $data[ $comment_id ]++; - $cookie = $this->serialize_cookie( $data ); - @setcookie( $this->_storagecookie, $cookie, time()+$this->cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN ); - if ( SITECOOKIEPATH != COOKIEPATH ) { - @setcookie( $this->_storagecookie, $cookie, time()+$this->cookie_lifetime, SITECOOKIEPATH, COOKIE_DOMAIN); - } - } else { - if ( ! isset( $data[ $comment_id ] ) ) { - $data[ $comment_id ] = 0; - } - $data[ $comment_id ]++; - $cookie = $this->serialize_cookie( $data ); - @setcookie( $this->_storagecookie, $cookie, time()+$this->cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN ); - if ( SITECOOKIEPATH != COOKIEPATH ) { - @setcookie( $this->_storagecookie, $cookie, time()+$this->cookie_lifetime, SITECOOKIEPATH, COOKIE_DOMAIN); - } - } - } - - $remote_addr = filter_input( INPUT_SERVER, 'REMOTE_ADDR', FILTER_VALIDATE_IP ); - $remote_addr = sanitize_text_field( $remote_addr ); - - // in case we don't have cookies. fall back to transients, block based on IP, shorter timeout to keep mem usage low and don't lock out whole companies - $transient = get_transient( md5( $this->_storagecookie . $remote_addr ) ); - if ( !$transient ) { - set_transient( md5( $this->_storagecookie . $remote_addr ), array( $comment_id => 1), $this->transient_lifetime ); - } else { - if ( ! isset( $transient[ $comment_id ] ) ) { - $transient[ $comment_id ] = 0; - } - $transient[ $comment_id ]++; - set_transient( md5( $this->_storagecookie . $remote_addr ), $transient, $this->transient_lifetime ); - } - - - $threshold = (int) get_option( $this->_plugin_prefix . '_threshold' ); - $current_reports = get_comment_meta( $comment_id, $this->_plugin_prefix . '_reported', true ); - $current_reports++; - update_comment_meta( $comment_id, $this->_plugin_prefix . '_reported', $current_reports ); - - - // we will not flag a comment twice. the moderator is the boss here. - $already_reported = get_comment_meta( $comment_id, $this->_plugin_prefix . '_reported', true ); - $already_moderated = get_comment_meta( $comment_id, $this->_plugin_prefix . '_moderated', true ); - if ( true == $already_reported && true == $already_moderated ) { - // But maybe the boss wants to allow comments to be reflagged - if ( ! apply_filters( 'safe_report_comments_allow_moderated_to_be_reflagged', false ) ) { - return; - } - } - - if ( $current_reports >= $threshold ) { - do_action( 'safe_report_comments_mark_flagged', $comment_id ); - wp_set_comment_status( $comment_id, 'hold' ); - } - } - - /* - * Die() with or without screen based on JS availability - */ - private function cond_die( $message ) { - if ( isset( $_REQUEST['no_js'] ) && true == (boolean) $_REQUEST['no_js'] ) { - wp_die( esc_html( $message ), esc_html("Safe Report Comments Notice"), array('response' => 200 ) ); - } else { - die( esc_html( $message ) ); - } - } - - /* - * Ajax callback to flag/report a comment - */ - public function flag_comment() { - if ( empty( $_REQUEST[ 'comment_id' ] ) || (int) $_REQUEST[ 'comment_id' ] != $_REQUEST[ 'comment_id' ] ) { - $this->cond_die( __( $this->invalid_values_message ) ); - } - - $comment_id = (int) $_REQUEST[ 'comment_id' ]; - if ( $this->already_flagged( $comment_id ) ) { - $this->cond_die( __( $this->already_flagged_message ) ); - } - - // checking if nonces help - if ( ! isset( $_REQUEST[ 'sc_nonce' ] ) || ! wp_verify_nonce( $_REQUEST[ 'sc_nonce' ], $this->_plugin_prefix . '_' . $this->_nonce_key ) ) { - $this->cond_die( __( $this->invalid_nonce_message ) ); - } else { - $this->mark_flagged( $comment_id ); - $this->cond_die( __( $this->thank_you_message ) ); - } - - } - - public function print_flagging_link( $comment_id='', $result_id='', $text='Report comment' ) { - //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaping done in get_flagging_link - echo $this->get_flagging_link( $comment_id='', $result_id='', $text='Report comment' ); - } - - /* - * Output Link to report a comment - */ - public function get_flagging_link( $comment_id='', $result_id='', $text='Report comment' ) { - global $in_comment_loop; - if ( empty( $comment_id ) && !$in_comment_loop ) { - return __( 'Wrong usage of print_flagging_link().' ); - } - if ( empty( $comment_id ) ) { - $comment_id = get_comment_ID(); - } - else { - $comment_id = (int) $comment_id; - if ( !get_comment( $comment_id ) ) { - return __( 'This comment does not exist.' ); - } - } - if ( empty( $result_id ) ) { - $result_id = 'safe-comments-result-' . $comment_id; - } - - $result_id = apply_filters( 'safe_report_comments_result_id', $result_id ); - $text = apply_filters( 'safe_report_comments_flagging_link_text', $text ); - - $nonce = wp_create_nonce( $this->_plugin_prefix . '_' . $this->_nonce_key ); - $params = array( - 'action' => 'safe_report_comments_flag_comment', - 'sc_nonce' => $nonce, - 'comment_id' => $comment_id, - 'result_id' => $result_id, - 'no_js' => true, - ); - - if ( $this->already_flagged( $comment_id ) ) { - return __( $this->already_flagged_note ); - } - - return apply_filters( 'safe_report_comments_flagging_link', ' - ' . esc_html( $text ) . '' ); - - - } - - /* - * Callback function to automatically hook in the report link after the comment reply link. - * If you want to control the placement on your own define no_autostart_safe_report_comments in your functions.php file and initialize the class - * with $safe_report_comments = new Safe_Report_Comments( $auto_init = false ); - */ - public function add_flagging_link( $comment_reply_link ) { - if ( !preg_match_all( '#^(.*)(]+>)(.+)()(.*)$#msiU', $comment_reply_link, $matches ) ) { - return '' . $comment_reply_link; - } - - $comment_reply_link = $matches[1][0] . $matches[2][0] . $matches[4][0] . $matches[5][0] . '' . $this->get_flagging_link() . '' . $matches[6][0]; - return apply_filters( 'safe_report_comments_comment_reply_link', $comment_reply_link ); - } - - /* - * Callback function to add the report counter to comments screen. Remove action manage_edit-comments_columns if not desired - */ - public function add_comment_reported_column( $comment_columns ) { - $comment_columns['comment_reported'] = _x('Reported', 'column name'); - return $comment_columns; - } - - /* - * Callback function to handle custom column. remove action manage_comments_custom_column if not desired - */ - public function manage_comment_reported_column( $column_name, $comment_id ) { - switch ( $column_name ) { - case 'comment_reported': - $reports = 0; - $already_reported = get_comment_meta( $comment_id, $this->_plugin_prefix . '_reported', true ); - if ( $already_reported > 0 ) { - $reports = (int) $already_reported; - } - echo esc_html( $reports ); - break; - default: - break; - } - } - - } +if ( ! class_exists( 'Safe_Report_Comments' ) ) { + require_once __DIR__ . '/class-safe-report-comments.php'; } -if ( !defined( 'no_autostart_safe_report_comments' ) ) { - $safe_report_comments = new Safe_Report_Comments; +if ( ! defined( 'no_autostart_safe_report_comments' ) ) { + $safe_report_comments = new Safe_Report_Comments(); } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index fc83ef7..7df12a4 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -14,4 +14,3 @@ function _manually_load_plugin() { require $_tests_dir . '/includes/bootstrap.php'; -require dirname( __FILE__ ) . '/safe-report-comments-testcase.php'; diff --git a/tests/safe-report-comments-testcase.php b/tests/safe-report-comments-testcase.php deleted file mode 100644 index dc890fc..0000000 --- a/tests/safe-report-comments-testcase.php +++ /dev/null @@ -1,13 +0,0 @@ -_toc = $safe_report_comments; - } -}